--- a/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java Fri Sep 06 12:10:30 2013 -0400
@@ -38,6 +38,7 @@
import sun.awt.*;
import sun.lwawt.macosx.*;
import sun.print.*;
+import sun.security.util.SecurityConstants;
public abstract class LWToolkit extends SunToolkit implements Runnable {
@@ -502,7 +503,7 @@
public Clipboard getSystemClipboard() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
- security.checkSystemClipboardAccess();
+ security.checkPermission(SecurityConstants.AWT.ACCESS_CLIPBOARD_PERMISSION);
}
synchronized (this) {
--- a/jdk/src/macosx/classes/sun/nio/ch/KQueueArrayWrapper.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/macosx/classes/sun/nio/ch/KQueueArrayWrapper.java Fri Sep 06 12:10:30 2013 -0400
@@ -87,6 +87,7 @@
private int incomingInterruptFD;
static {
+ IOUtil.load();
initStructSizes();
String datamodel = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("sun.arch.data.model")
--- a/jdk/src/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -246,9 +246,4 @@
}
return this;
}
-
-
- static {
- Util.load();
- }
}
--- a/jdk/src/share/back/SDE.c Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/back/SDE.c Fri Sep 06 12:10:30 2013 -0400
@@ -28,6 +28,12 @@
#include "util.h"
#include "SDE.h"
+#ifdef __APPLE__
+/* use setjmp/longjmp versions that do not save/restore the signal mask */
+#define setjmp _setjmp
+#define longjmp _longjmp
+#endif
+
/**
* This SourceDebugExtension code does not
* allow concurrent translation - due to caching method.
--- a/jdk/src/share/classes/java/awt/TextComponent.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/awt/TextComponent.java Fri Sep 06 12:10:30 2013 -0400
@@ -35,6 +35,7 @@
import javax.swing.text.AttributeSet;
import javax.accessibility.*;
import java.awt.im.InputMethodRequests;
+import sun.security.util.SecurityConstants;
/**
* The <code>TextComponent</code> class is the superclass of
@@ -728,7 +729,7 @@
SecurityManager sm = System.getSecurityManager();
if (sm == null) return true;
try {
- sm.checkSystemClipboardAccess();
+ sm.checkPermission(SecurityConstants.AWT.ACCESS_CLIPBOARD_PERMISSION);
return true;
} catch (SecurityException e) {}
return false;
--- a/jdk/src/share/classes/java/awt/Toolkit.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/awt/Toolkit.java Fri Sep 06 12:10:30 2013 -0400
@@ -1270,12 +1270,8 @@
* <p>
* Each actual implementation of this method should first check if there
* is a security manager installed. If there is, the method should call
- * the security manager's <code>checkSystemClipboardAccess</code> method
- * to ensure it's ok to to access the system clipboard. If the default
- * implementation of <code>checkSystemClipboardAccess</code> is used (that
- * is, that method is not overriden), then this results in a call to the
- * security manager's <code>checkPermission</code> method with an <code>
- * AWTPermission("accessClipboard")</code> permission.
+ * the security manager's {@link SecurityManager#checkPermission
+ * checkPermission} method to check {@code AWTPermission("accessClipboard")}.
*
* @return the system Clipboard
* @exception HeadlessException if GraphicsEnvironment.isHeadless()
@@ -1318,14 +1314,9 @@
* system selection <code>Clipboard</code> as described above.
* <p>
* Each actual implementation of this method should first check if there
- * is a <code>SecurityManager</code> installed. If there is, the method
- * should call the <code>SecurityManager</code>'s
- * <code>checkSystemClipboardAccess</code> method to ensure that client
- * code has access the system selection. If the default implementation of
- * <code>checkSystemClipboardAccess</code> is used (that is, if the method
- * is not overridden), then this results in a call to the
- * <code>SecurityManager</code>'s <code>checkPermission</code> method with
- * an <code>AWTPermission("accessClipboard")</code> permission.
+ * is a security manager installed. If there is, the method should call
+ * the security manager's {@link SecurityManager#checkPermission
+ * checkPermission} method to check {@code AWTPermission("accessClipboard")}.
*
* @return the system selection as a <code>Clipboard</code>, or
* <code>null</code> if the native platform does not support a
@@ -1699,25 +1690,20 @@
* therefore not assume that the EventQueue instance returned
* by this method will be shared by other applets or the system.
*
- * <p>First, if there is a security manager, its
- * <code>checkAwtEventQueueAccess</code>
- * method is called.
- * If the default implementation of <code>checkAwtEventQueueAccess</code>
- * is used (that is, that method is not overriden), then this results in
- * a call to the security manager's <code>checkPermission</code> method
- * with an <code>AWTPermission("accessEventQueue")</code> permission.
+ * <p> If there is a security manager then its
+ * {@link SecurityManager#checkPermission checkPermission} method
+ * is called to check {@code AWTPermission("accessEventQueue")}.
*
* @return the <code>EventQueue</code> object
* @throws SecurityException
- * if a security manager exists and its <code>{@link
- * java.lang.SecurityManager#checkAwtEventQueueAccess}</code>
- * method denies access to the <code>EventQueue</code>
+ * if a security manager is set and it denies access to
+ * the {@code EventQueue}
* @see java.awt.AWTPermission
*/
public final EventQueue getSystemEventQueue() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
- security.checkAwtEventQueueAccess();
+ security.checkPermission(SecurityConstants.AWT.CHECK_AWT_EVENTQUEUE_PERMISSION);
}
return getSystemEventQueueImpl();
}
--- a/jdk/src/share/classes/java/awt/Window.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/awt/Window.java Fri Sep 06 12:10:30 2013 -0400
@@ -195,10 +195,9 @@
/**
* This represents the warning message that is
* to be displayed in a non secure window. ie :
- * a window that has a security manager installed for
- * which calling SecurityManager.checkTopLevelWindow()
- * is false. This message can be displayed anywhere in
- * the window.
+ * a window that has a security manager installed that denies
+ * {@code AWTPermission("showWindowWithoutWarningBanner")}.
+ * This message can be displayed anywhere in the window.
*
* @serial
* @see #getWarningString
@@ -417,11 +416,10 @@
* Constructs a new, initially invisible window in default size with the
* specified {@code GraphicsConfiguration}.
* <p>
- * If there is a security manager, this method first calls
- * the security manager's {@code checkTopLevelWindow}
- * method with {@code this}
- * as its argument to determine whether or not the window
- * must be displayed with a warning banner.
+ * If there is a security manager, then it is invoked to check
+ * {@code AWTPermission("showWindowWithoutWarningBanner")}
+ * to determine whether or not the window must be displayed with
+ * a warning banner.
*
* @param gc the {@code GraphicsConfiguration} of the target screen
* device. If {@code gc} is {@code null}, the system default
@@ -432,7 +430,6 @@
* {@code GraphicsEnvironment.isHeadless()} returns {@code true}
*
* @see java.awt.GraphicsEnvironment#isHeadless
- * @see java.lang.SecurityManager#checkTopLevelWindow
*/
Window(GraphicsConfiguration gc) {
init(gc);
@@ -511,25 +508,16 @@
/**
* Constructs a new, initially invisible window in the default size.
- *
- * <p>First, if there is a security manager, its
- * {@code checkTopLevelWindow}
- * method is called with {@code this}
- * as its argument
- * to see if it's ok to display the window without a warning banner.
- * If the default implementation of {@code checkTopLevelWindow}
- * is used (that is, that method is not overriden), then this results in
- * a call to the security manager's {@code checkPermission} method
- * with an {@code AWTPermission("showWindowWithoutWarningBanner")}
- * permission. It that method raises a SecurityException,
- * {@code checkTopLevelWindow} returns false, otherwise it
- * returns true. If it returns false, a warning banner is created.
+ * <p>
+ * If there is a security manager set, it is invoked to check
+ * {@code AWTPermission("showWindowWithoutWarningBanner")}.
+ * If that check fails with a {@code SecurityException} then a warning
+ * banner is created.
*
* @exception HeadlessException when
* {@code GraphicsEnvironment.isHeadless()} returns {@code true}
*
* @see java.awt.GraphicsEnvironment#isHeadless
- * @see java.lang.SecurityManager#checkTopLevelWindow
*/
Window() throws HeadlessException {
GraphicsEnvironment.checkHeadless();
@@ -541,11 +529,10 @@
* {@code Frame} as its owner. The window will not be focusable
* unless its owner is showing on the screen.
* <p>
- * If there is a security manager, this method first calls
- * the security manager's {@code checkTopLevelWindow}
- * method with {@code this}
- * as its argument to determine whether or not the window
- * must be displayed with a warning banner.
+ * If there is a security manager set, it is invoked to check
+ * {@code AWTPermission("showWindowWithoutWarningBanner")}.
+ * If that check fails with a {@code SecurityException} then a warning
+ * banner is created.
*
* @param owner the {@code Frame} to act as owner or {@code null}
* if this window has no owner
@@ -555,7 +542,6 @@
* {@code GraphicsEnvironment.isHeadless} returns {@code true}
*
* @see java.awt.GraphicsEnvironment#isHeadless
- * @see java.lang.SecurityManager#checkTopLevelWindow
* @see #isShowing
*/
public Window(Frame owner) {
@@ -570,11 +556,10 @@
* unless its nearest owning {@code Frame} or {@code Dialog}
* is showing on the screen.
* <p>
- * If there is a security manager, this method first calls
- * the security manager's {@code checkTopLevelWindow}
- * method with {@code this}
- * as its argument to determine whether or not the window
- * must be displayed with a warning banner.
+ * If there is a security manager set, it is invoked to check
+ * {@code AWTPermission("showWindowWithoutWarningBanner")}.
+ * If that check fails with a {@code SecurityException} then a
+ * warning banner is created.
*
* @param owner the {@code Window} to act as owner or
* {@code null} if this window has no owner
@@ -585,7 +570,6 @@
* {@code true}
*
* @see java.awt.GraphicsEnvironment#isHeadless
- * @see java.lang.SecurityManager#checkTopLevelWindow
* @see #isShowing
*
* @since 1.2
@@ -603,11 +587,10 @@
* its nearest owning {@code Frame} or {@code Dialog}
* is showing on the screen.
* <p>
- * If there is a security manager, this method first calls
- * the security manager's {@code checkTopLevelWindow}
- * method with {@code this}
- * as its argument to determine whether or not the window
- * must be displayed with a warning banner.
+ * If there is a security manager set, it is invoked to check
+ * {@code AWTPermission("showWindowWithoutWarningBanner")}. If that
+ * check fails with a {@code SecurityException} then a warning banner
+ * is created.
*
* @param owner the window to act as owner or {@code null}
* if this window has no owner
@@ -621,7 +604,6 @@
* {@code true}
*
* @see java.awt.GraphicsEnvironment#isHeadless
- * @see java.lang.SecurityManager#checkTopLevelWindow
* @see GraphicsConfiguration#getBounds
* @see #isShowing
* @since 1.3
@@ -1362,10 +1344,9 @@
* Gets the warning string that is displayed with this window.
* If this window is insecure, the warning string is displayed
* somewhere in the visible area of the window. A window is
- * insecure if there is a security manager, and the security
- * manager's {@code checkTopLevelWindow} method returns
- * {@code false} when this window is passed to it as an
- * argument.
+ * insecure if there is a security manager and the security
+ * manager denies
+ * {@code AWTPermission("showWindowWithoutWarningBanner")}.
* <p>
* If the window is secure, then {@code getWarningString}
* returns {@code null}. If the window is insecure, this
@@ -1373,7 +1354,6 @@
* {@code awt.appletWarning}
* and returns the string value of that property.
* @return the warning string for this window.
- * @see java.lang.SecurityManager#checkTopLevelWindow(java.lang.Object)
*/
public final String getWarningString() {
return warningString;
@@ -1383,10 +1363,12 @@
warningString = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
- if (!sm.checkTopLevelWindow(this)) {
+ try {
+ sm.checkPermission(SecurityConstants.AWT.TOPLEVEL_WINDOW_PERMISSION);
+ } catch (SecurityException se) {
// make sure the privileged action is only
// for getting the property! We don't want the
- // above checkTopLevelWindow call to always succeed!
+ // above checkPermission call to always succeed!
warningString = AccessController.doPrivileged(
new GetPropertyAction("awt.appletWarning",
"Java Applet Window"));
--- a/jdk/src/share/classes/java/awt/event/InputEvent.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/awt/event/InputEvent.java Fri Sep 06 12:10:30 2013 -0400
@@ -33,6 +33,7 @@
import sun.awt.AWTAccessor;
import sun.util.logging.PlatformLogger;
+import sun.security.util.SecurityConstants;
/**
* The root event class for all component-level input events.
@@ -350,7 +351,7 @@
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
- sm.checkSystemClipboardAccess();
+ sm.checkPermission(SecurityConstants.AWT.ACCESS_CLIPBOARD_PERMISSION);
b = true;
} catch (SecurityException se) {
if (logger.isLoggable(PlatformLogger.Level.FINE)) {
--- a/jdk/src/share/classes/java/io/Console.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/io/Console.java Fri Sep 06 12:10:30 2013 -0400
@@ -124,9 +124,11 @@
* {@link java.io.Reader#read(java.nio.CharBuffer) read(java.nio.CharBuffer)}
* on the returned object will not read in characters beyond the line
* bound for each invocation, even if the destination buffer has space for
- * more characters. A line bound is considered to be any one of a line feed
- * (<tt>'\n'</tt>), a carriage return (<tt>'\r'</tt>), a carriage return
- * followed immediately by a linefeed, or an end of stream.
+ * more characters. The {@code Reader}'s {@code read} methods may block if a
+ * line bound has not been entered or reached on the console's input device.
+ * A line bound is considered to be any one of a line feed (<tt>'\n'</tt>),
+ * a carriage return (<tt>'\r'</tt>), a carriage return followed immediately
+ * by a linefeed, or an end of stream.
*
* @return The reader associated with this console
*/
--- a/jdk/src/share/classes/java/lang/AutoCloseable.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/AutoCloseable.java Fri Sep 06 12:10:30 2013 -0400
@@ -26,7 +26,24 @@
package java.lang;
/**
- * A resource that must be closed when it is no longer needed.
+ * An object that may hold resources (such as file or socket handles)
+ * until it is closed. The {@link #close()} method of an {@code AutoCloseable}
+ * object is called automatically when exiting a {@code
+ * try}-with-resources block for which the object has been declared in
+ * the resource specification header. This construction ensures prompt
+ * release, avoiding resource exhaustion exceptions and errors that
+ * may otherwise occur.
+ *
+ * @apiNote
+ * <p>It is possible, and in fact common, for a base class to
+ * implement AutoCloseable even though not all of its subclasses or
+ * instances will hold releasable resources. For code that must operate
+ * in complete generality, or when it is known that the {@code AutoCloseable}
+ * instance requires resource release, it is recommended to use {@code
+ * try}-with-resources constructions. However, when using facilities such as
+ * {@link java.util.stream.Stream} that support both I/O-based and
+ * non-I/O-based forms, {@code try}-with-resources blocks are in
+ * general unnecessary when using non-I/O-based forms.
*
* @author Josh Bloch
* @since 1.7
--- a/jdk/src/share/classes/java/lang/Class.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/Class.java Fri Sep 06 12:10:30 2013 -0400
@@ -1484,22 +1484,24 @@
/**
* Returns an array containing {@code Field} objects reflecting all
* the accessible public fields of the class or interface represented by
- * this {@code Class} object. The elements in the array returned are
- * not sorted and are not in any particular order. This method returns an
- * array of length 0 if the class or interface has no accessible public
- * fields, or if it represents an array class, a primitive type, or void.
+ * this {@code Class} object.
+ *
+ * <p> If this {@code Class} object represents a class or interface with no
+ * no accessible public fields, then this method returns an array of length
+ * 0.
+ *
+ * <p> If this {@code Class} object represents a class, then this method
+ * returns the public fields of the class and of all its superclasses.
*
- * <p> Specifically, if this {@code Class} object represents a class,
- * this method returns the public fields of this class and of all its
- * superclasses. If this {@code Class} object represents an
- * interface, this method returns the fields of this interface and of all
- * its superinterfaces.
+ * <p> If this {@code Class} object represents an interface, then this
+ * method returns the fields of the interface and of all its
+ * superinterfaces.
*
- * <p> The implicit length field for array class is not reflected by this
- * method. User code should use the methods of class {@code Array} to
- * manipulate arrays.
+ * <p> If this {@code Class} object represents an array type, a primitive
+ * type, or void, then this method returns an array of length 0.
*
- * <p> See <em>The Java Language Specification</em>, sections 8.2 and 8.3.
+ * <p> The elements in the array returned are not sorted and are not in any
+ * particular order.
*
* @return the array of {@code Field} objects representing the
* public fields
@@ -1512,6 +1514,8 @@
* of this class.
*
* @since JDK1.1
+ * @jls 8.2 Class Members
+ * @jls 8.3 Field Declarations
*/
@CallerSensitive
public Field[] getFields() throws SecurityException {
@@ -1595,13 +1599,14 @@
/**
- * Returns a {@code Field} object that reflects the specified public
- * member field of the class or interface represented by this
- * {@code Class} object. The {@code name} parameter is a
- * {@code String} specifying the simple name of the desired field.
+ * Returns a {@code Field} object that reflects the specified public member
+ * field of the class or interface represented by this {@code Class}
+ * object. The {@code name} parameter is a {@code String} specifying the
+ * simple name of the desired field.
*
* <p> The field to be reflected is determined by the algorithm that
- * follows. Let C be the class represented by this object:
+ * follows. Let C be the class or interface represented by this object:
+ *
* <OL>
* <LI> If C declares a public field with the name specified, that is the
* field to be reflected.</LI>
@@ -1614,7 +1619,8 @@
* is thrown.</LI>
* </OL>
*
- * <p> See <em>The Java Language Specification</em>, sections 8.2 and 8.3.
+ * <p> If this {@code Class} object represents an array type, then this
+ * method does not find the {@code length} field of the array type.
*
* @param name the field name
* @return the {@code Field} object of this class specified by
@@ -1631,6 +1637,8 @@
* of this class.
*
* @since JDK1.1
+ * @jls 8.2 Class Members
+ * @jls 8.3 Field Declarations
*/
@CallerSensitive
public Field getField(String name)
@@ -1800,12 +1808,15 @@
* declared by the class or interface represented by this
* {@code Class} object. This includes public, protected, default
* (package) access, and private fields, but excludes inherited fields.
- * The elements in the array returned are not sorted and are not in any
- * particular order. This method returns an array of length 0 if the class
- * or interface declares no fields, or if this {@code Class} object
- * represents a primitive type, an array class, or void.
+ *
+ * <p> If this {@code Class} object represents a class or interface with no
+ * declared fields, then this method returns an array of length 0.
*
- * <p> See <em>The Java Language Specification</em>, sections 8.2 and 8.3.
+ * <p> If this {@code Class} object represents an array type, a primitive
+ * type, or void, then this method returns an array of length 0.
+ *
+ * <p> The elements in the array returned are not sorted and are not in any
+ * particular order.
*
* @return the array of {@code Field} objects representing all the
* declared fields of this class
@@ -1831,6 +1842,8 @@
* </ul>
*
* @since JDK1.1
+ * @jls 8.2 Class Members
+ * @jls 8.3 Field Declarations
*/
@CallerSensitive
public Field[] getDeclaredFields() throws SecurityException {
@@ -1935,9 +1948,11 @@
/**
* Returns a {@code Field} object that reflects the specified declared
* field of the class or interface represented by this {@code Class}
- * object. The {@code name} parameter is a {@code String} that
- * specifies the simple name of the desired field. Note that this method
- * will not reflect the {@code length} field of an array class.
+ * object. The {@code name} parameter is a {@code String} that specifies
+ * the simple name of the desired field.
+ *
+ * <p> If this {@code Class} object represents an array type, then this
+ * method does not find the {@code length} field of the array type.
*
* @param name the name of the field
* @return the {@code Field} object for the specified field in this
@@ -1967,6 +1982,8 @@
* </ul>
*
* @since JDK1.1
+ * @jls 8.2 Class Members
+ * @jls 8.3 Field Declarations
*/
@CallerSensitive
public Field getDeclaredField(String name)
--- a/jdk/src/share/classes/java/lang/SecurityManager.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/SecurityManager.java Fri Sep 06 12:10:30 2013 -0400
@@ -1336,9 +1336,16 @@
* top-level windows; <code>false</code> otherwise.
* @exception NullPointerException if the <code>window</code> argument is
* <code>null</code>.
+ * @deprecated The dependency on {@code AWTPermission} creates an
+ * impediment to future modularization of the Java platform.
+ * Users of this method should instead invoke
+ * {@link #checkPermission} directly.
+ * This method will be changed in a future release to check
+ * the permission {@code java.security.AllPermission}.
* @see java.awt.Window
* @see #checkPermission(java.security.Permission) checkPermission
*/
+ @Deprecated
public boolean checkTopLevelWindow(Object window) {
if (window == null) {
throw new NullPointerException("window can't be null");
@@ -1398,8 +1405,15 @@
* @since JDK1.1
* @exception SecurityException if the calling thread does not have
* permission to access the system clipboard.
+ * @deprecated The dependency on {@code AWTPermission} creates an
+ * impediment to future modularization of the Java platform.
+ * Users of this method should instead invoke
+ * {@link #checkPermission} directly.
+ * This method will be changed in a future release to check
+ * the permission {@code java.security.AllPermission}.
* @see #checkPermission(java.security.Permission) checkPermission
*/
+ @Deprecated
public void checkSystemClipboardAccess() {
Permission perm = SecurityConstants.AWT.ACCESS_CLIPBOARD_PERMISSION;
if (perm == null) {
@@ -1427,8 +1441,15 @@
* @since JDK1.1
* @exception SecurityException if the calling thread does not have
* permission to access the AWT event queue.
+ * @deprecated The dependency on {@code AWTPermission} creates an
+ * impediment to future modularization of the Java platform.
+ * Users of this method should instead invoke
+ * {@link #checkPermission} directly.
+ * This method will be changed in a future release to check
+ * the permission {@code java.security.AllPermission}.
* @see #checkPermission(java.security.Permission) checkPermission
*/
+ @Deprecated
public void checkAwtEventQueueAccess() {
Permission perm = SecurityConstants.AWT.CHECK_AWT_EVENTQUEUE_PERMISSION;
if (perm == null) {
--- a/jdk/src/share/classes/java/lang/String.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/String.java Fri Sep 06 12:10:30 2013 -0400
@@ -2457,8 +2457,8 @@
* String message = String.join(" ", strings);
* //message returned is: "Java is cool"
*
- * Set<String> strings = new HashSet<>();
- * Strings.add("Java"); strings.add("is");
+ * Set<String> strings = new LinkedHashSet<>();
+ * strings.add("Java"); strings.add("is");
* strings.add("very"); strings.add("cool");
* String message = String.join("-", strings);
* //message returned is: "Java-is-very-cool"
@@ -2652,7 +2652,7 @@
* returns {@code "t\u005Cu0131tle"}, where '\u005Cu0131' is the
* LATIN SMALL LETTER DOTLESS I character.
* To obtain correct results for locale insensitive strings, use
- * {@code toLowerCase(Locale.ENGLISH)}.
+ * {@code toLowerCase(Locale.ROOT)}.
* <p>
* @return the {@code String}, converted to lowercase.
* @see java.lang.String#toLowerCase(Locale)
@@ -2815,7 +2815,7 @@
* returns {@code "T\u005Cu0130TLE"}, where '\u005Cu0130' is the
* LATIN CAPITAL LETTER I WITH DOT ABOVE character.
* To obtain correct results for locale insensitive strings, use
- * {@code toUpperCase(Locale.ENGLISH)}.
+ * {@code toUpperCase(Locale.ROOT)}.
* <p>
* @return the {@code String}, converted to uppercase.
* @see java.lang.String#toUpperCase(Locale)
--- a/jdk/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Fri Sep 06 12:10:30 2013 -0400
@@ -124,7 +124,7 @@
this.samMethodType = samMethodType;
this.implMethod = implMethod;
- this.implInfo = new MethodHandleInfo(implMethod);
+ this.implInfo = caller.revealDirect(implMethod);
// @@@ Temporary work-around pending resolution of 8005119
this.implKind = (implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial)
? MethodHandleInfo.REF_invokeVirtual
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/lang/invoke/InfoFromMemberName.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import java.security.*;
+import java.lang.reflect.*;
+import java.lang.invoke.MethodHandleNatives.Constants;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandleStatics.*;
+
+/*
+ * Auxiliary to MethodHandleInfo, wants to nest in MethodHandleInfo but must be non-public.
+ */
+/*non-public*/
+final
+class InfoFromMemberName implements MethodHandleInfo {
+ private final MemberName member;
+ private final int referenceKind;
+
+ InfoFromMemberName(Lookup lookup, MemberName member, byte referenceKind) {
+ assert(member.isResolved() || member.isMethodHandleInvoke());
+ assert(member.referenceKindIsConsistentWith(referenceKind));
+ this.member = member;
+ this.referenceKind = referenceKind;
+ }
+
+ @Override
+ public Class<?> getDeclaringClass() {
+ return member.getDeclaringClass();
+ }
+
+ @Override
+ public String getName() {
+ return member.getName();
+ }
+
+ @Override
+ public MethodType getMethodType() {
+ return member.getMethodOrFieldType();
+ }
+
+ @Override
+ public int getModifiers() {
+ return member.getModifiers();
+ }
+
+ @Override
+ public int getReferenceKind() {
+ return referenceKind;
+ }
+
+ @Override
+ public String toString() {
+ return MethodHandleInfo.toString(getReferenceKind(), getDeclaringClass(), getName(), getMethodType());
+ }
+
+ @Override
+ public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup) {
+ if (member.isMethodHandleInvoke() && !member.isVarargs()) {
+ // This member is an instance of a signature-polymorphic method, which cannot be reflected
+ // A method handle invoker can come in either of two forms:
+ // A generic placeholder (present in the source code, and varargs)
+ // and a signature-polymorphic instance (synthetic and not varargs).
+ // For more information see comments on {@link MethodHandleNatives#linkMethod}.
+ throw new IllegalArgumentException("cannot reflect signature polymorphic method");
+ }
+ Member mem = AccessController.doPrivileged(new PrivilegedAction<Member>() {
+ public Member run() {
+ try {
+ return reflectUnchecked();
+ } catch (ReflectiveOperationException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+ });
+ try {
+ Class<?> defc = getDeclaringClass();
+ byte refKind = (byte) getReferenceKind();
+ lookup.checkAccess(refKind, defc, convertToMemberName(refKind, mem));
+ } catch (IllegalAccessException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ return expected.cast(mem);
+ }
+
+ private Member reflectUnchecked() throws ReflectiveOperationException {
+ byte refKind = (byte) getReferenceKind();
+ Class<?> defc = getDeclaringClass();
+ boolean isPublic = Modifier.isPublic(getModifiers());
+ if (MethodHandleNatives.refKindIsMethod(refKind)) {
+ if (isPublic)
+ return defc.getMethod(getName(), getMethodType().parameterArray());
+ else
+ return defc.getDeclaredMethod(getName(), getMethodType().parameterArray());
+ } else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
+ if (isPublic)
+ return defc.getConstructor(getMethodType().parameterArray());
+ else
+ return defc.getDeclaredConstructor(getMethodType().parameterArray());
+ } else if (MethodHandleNatives.refKindIsField(refKind)) {
+ if (isPublic)
+ return defc.getField(getName());
+ else
+ return defc.getDeclaredField(getName());
+ } else {
+ throw new IllegalArgumentException("referenceKind="+refKind);
+ }
+ }
+
+ private static MemberName convertToMemberName(byte refKind, Member mem) throws IllegalAccessException {
+ if (mem instanceof Method) {
+ boolean wantSpecial = (refKind == REF_invokeSpecial);
+ return new MemberName((Method) mem, wantSpecial);
+ } else if (mem instanceof Constructor) {
+ return new MemberName((Constructor) mem);
+ } else if (mem instanceof Field) {
+ boolean isSetter = (refKind == REF_putField || refKind == REF_putStatic);
+ return new MemberName((Field) mem, isSetter);
+ }
+ throw new InternalError(mem.getClass().getName());
+ }
+}
--- a/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Fri Sep 06 12:10:30 2013 -0400
@@ -612,6 +612,12 @@
return false; // inner class of some sort
if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
return false; // not on BCP
+ MethodType mtype = member.getMethodOrFieldType();
+ if (!isStaticallyNameable(mtype.returnType()))
+ return false;
+ for (Class<?> ptype : mtype.parameterArray())
+ if (!isStaticallyNameable(ptype))
+ return false;
if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls))
return true; // in java.lang.invoke package
if (member.isPublic() && isStaticallyNameable(cls))
--- a/jdk/src/share/classes/java/lang/invoke/Invokers.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/invoke/Invokers.java Fri Sep 06 12:10:30 2013 -0400
@@ -87,6 +87,7 @@
lform = invokeForm(mtype, true, MethodTypeForm.LF_EX_INVOKER);
invoker = SimpleMethodHandle.make(invokerType, lform);
}
+ invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invokeExact", mtype));
assert(checkInvoker(invoker));
exactInvoker = invoker;
return invoker;
@@ -110,6 +111,7 @@
lform = invokeForm(mtype, true, MethodTypeForm.LF_GEN_INVOKER);
invoker = SimpleMethodHandle.make(invokerType, lform);
}
+ invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invoke", mtype));
assert(checkInvoker(invoker));
generalInvoker = invoker;
return invoker;
--- a/jdk/src/share/classes/java/lang/invoke/MemberName.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/invoke/MemberName.java Fri Sep 06 12:10:30 2013 -0400
@@ -320,14 +320,18 @@
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */
public boolean isMethodHandleInvoke() {
- final int bits = Modifier.NATIVE | Modifier.FINAL;
+ final int bits = MH_INVOKE_MODS;
final int negs = Modifier.STATIC;
if (testFlags(bits | negs, bits) &&
clazz == MethodHandle.class) {
- return name.equals("invoke") || name.equals("invokeExact");
+ return isMethodHandleInvokeName(name);
}
return false;
}
+ public static boolean isMethodHandleInvokeName(String name) {
+ return name.equals("invoke") || name.equals("invokeExact");
+ }
+ private static final int MH_INVOKE_MODS = Modifier.NATIVE | Modifier.FINAL | Modifier.PUBLIC;
/** Utility method to query the modifier flags of this member. */
public boolean isStatic() {
@@ -482,12 +486,27 @@
m.getClass(); // NPE check
// fill in vmtarget, vmindex while we have m in hand:
MethodHandleNatives.init(this, m);
+ if (clazz == null) { // MHN.init failed
+ if (m.getDeclaringClass() == MethodHandle.class &&
+ isMethodHandleInvokeName(m.getName())) {
+ // The JVM did not reify this signature-polymorphic instance.
+ // Need a special case here.
+ // See comments on MethodHandleNatives.linkMethod.
+ MethodType type = MethodType.methodType(m.getReturnType(), m.getParameterTypes());
+ int flags = flagsMods(IS_METHOD, m.getModifiers(), REF_invokeVirtual);
+ init(MethodHandle.class, m.getName(), type, flags);
+ if (isMethodHandleInvoke())
+ return;
+ }
+ throw new LinkageError(m.toString());
+ }
assert(isResolved() && this.clazz != null);
this.name = m.getName();
if (this.type == null)
this.type = new Object[] { m.getReturnType(), m.getParameterTypes() };
if (wantSpecial) {
- assert(!isAbstract()) : this;
+ if (isAbstract())
+ throw new AbstractMethodError(this.toString());
if (getReferenceKind() == REF_invokeVirtual)
changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
else if (getReferenceKind() == REF_invokeInterface)
@@ -562,6 +581,22 @@
initResolved(true);
}
+ /**
+ * Create a name for a signature-polymorphic invoker.
+ * This is a placeholder for a signature-polymorphic instance
+ * (of MH.invokeExact, etc.) that the JVM does not reify.
+ * See comments on {@link MethodHandleNatives#linkMethod}.
+ */
+ static MemberName makeMethodHandleInvoke(String name, MethodType type) {
+ return makeMethodHandleInvoke(name, type, MH_INVOKE_MODS | SYNTHETIC);
+ }
+ static MemberName makeMethodHandleInvoke(String name, MethodType type, int mods) {
+ MemberName mem = new MemberName(MethodHandle.class, name, type, REF_invokeVirtual);
+ mem.flags |= mods; // it's not resolved, but add these modifiers anyway
+ assert(mem.isMethodHandleInvoke()) : mem;
+ return mem;
+ }
+
// bare-bones constructor; the JVM will fill it in
MemberName() { }
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Fri Sep 06 12:10:30 2013 -0400
@@ -1285,6 +1285,21 @@
}
/*non-public*/
+ MethodHandle withInternalMemberName(MemberName member) {
+ if (member != null) {
+ return MethodHandleImpl.makeWrappedMember(this, member);
+ } else if (internalMemberName() == null) {
+ // The required internaMemberName is null, and this MH (like most) doesn't have one.
+ return this;
+ } else {
+ // The following case is rare. Mask the internalMemberName by wrapping the MH in a BMH.
+ MethodHandle result = rebind();
+ assert (result.internalMemberName() == null);
+ return result;
+ }
+ }
+
+ /*non-public*/
boolean isInvokeSpecial() {
return false; // DMH.Special returns true
}
@@ -1356,7 +1371,7 @@
MethodHandle rebind() {
// Bind 'this' into a new invoker, of the known class BMH.
MethodType type2 = type();
- LambdaForm form2 = reinvokerForm(type2.basicType());
+ LambdaForm form2 = reinvokerForm(this);
// form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }
return BoundMethodHandle.bindSingle(type2, form2, this);
}
@@ -1369,23 +1384,38 @@
/** Create a LF which simply reinvokes a target of the given basic type.
* The target MH must override {@link #reinvokerTarget} to provide the target.
*/
- static LambdaForm reinvokerForm(MethodType mtype) {
- mtype = mtype.basicType();
+ static LambdaForm reinvokerForm(MethodHandle target) {
+ MethodType mtype = target.type().basicType();
LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE);
if (reinvoker != null) return reinvoker;
- MethodHandle MH_invokeBasic = MethodHandles.basicInvoker(mtype);
+ if (mtype.parameterSlotCount() >= MethodType.MAX_MH_ARITY)
+ return makeReinvokerForm(target.type(), target); // cannot cache this
+ reinvoker = makeReinvokerForm(mtype, null);
+ return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, reinvoker);
+ }
+ private static LambdaForm makeReinvokerForm(MethodType mtype, MethodHandle customTargetOrNull) {
+ boolean customized = (customTargetOrNull != null);
+ MethodHandle MH_invokeBasic = customized ? null : MethodHandles.basicInvoker(mtype);
final int THIS_BMH = 0;
final int ARG_BASE = 1;
final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
int nameCursor = ARG_LIMIT;
- final int NEXT_MH = nameCursor++;
+ final int NEXT_MH = customized ? -1 : nameCursor++;
final int REINVOKE = nameCursor++;
LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
- names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
- Object[] targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
- targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
- names[REINVOKE] = new LambdaForm.Name(MH_invokeBasic, targetArgs);
- return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, new LambdaForm("BMH.reinvoke", ARG_LIMIT, names));
+ Object[] targetArgs;
+ MethodHandle targetMH;
+ if (customized) {
+ targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class);
+ targetMH = customTargetOrNull;
+ } else {
+ names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]);
+ targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class);
+ targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH
+ targetMH = MethodHandles.basicInvoker(mtype);
+ }
+ names[REINVOKE] = new LambdaForm.Name(targetMH, targetArgs);
+ return new LambdaForm("BMH.reinvoke", ARG_LIMIT, names);
}
private static final LambdaForm.NamedFunction NF_reinvokerTarget;
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -317,7 +317,7 @@
private MethodHandle cache;
AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) {
- super(type, reinvokerForm(type));
+ super(type, reinvokerForm(target));
this.target = target;
this.arrayType = arrayType;
this.cache = target.asCollector(arrayType, 0);
@@ -778,16 +778,27 @@
}
static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
- static MethodHandle FAKE_METHOD_HANDLE_INVOKE;
- static
- MethodHandle fakeMethodHandleInvoke(MemberName method) {
- MethodType type = method.getInvocationType();
- assert(type.equals(MethodType.methodType(Object.class, Object[].class)));
- MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE;
+ static MethodHandle[] FAKE_METHOD_HANDLE_INVOKE = new MethodHandle[2];
+ static MethodHandle fakeMethodHandleInvoke(MemberName method) {
+ int idx;
+ assert(method.isMethodHandleInvoke());
+ switch (method.getName()) {
+ case "invoke": idx = 0; break;
+ case "invokeExact": idx = 1; break;
+ default: throw new InternalError(method.getName());
+ }
+ MethodHandle mh = FAKE_METHOD_HANDLE_INVOKE[idx];
if (mh != null) return mh;
- mh = throwException(type.insertParameterTypes(0, UnsupportedOperationException.class));
+ MethodType type = MethodType.methodType(Object.class, UnsupportedOperationException.class,
+ MethodHandle.class, Object[].class);
+ mh = throwException(type);
mh = mh.bindTo(new UnsupportedOperationException("cannot reflectively invoke MethodHandle"));
- FAKE_METHOD_HANDLE_INVOKE = mh;
+ if (!method.getInvocationType().equals(mh.type()))
+ throw new InternalError(method.toString());
+ mh = mh.withInternalMemberName(method);
+ mh = mh.asVarargsCollector(Object[].class);
+ assert(method.isVarargs());
+ FAKE_METHOD_HANDLE_INVOKE[idx] = mh;
return mh;
}
@@ -821,7 +832,7 @@
MethodHandle vamh = prepareForInvoker(mh);
// Cache the result of makeInjectedInvoker once per argument class.
MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass);
- return restoreToType(bccInvoker.bindTo(vamh), mh.type());
+ return restoreToType(bccInvoker.bindTo(vamh), mh.type(), mh.internalMemberName());
}
private static MethodHandle makeInjectedInvoker(Class<?> hostClass) {
@@ -876,8 +887,11 @@
}
// Undo the adapter effect of prepareForInvoker:
- private static MethodHandle restoreToType(MethodHandle vamh, MethodType type) {
- return vamh.asCollector(Object[].class, type.parameterCount()).asType(type);
+ private static MethodHandle restoreToType(MethodHandle vamh, MethodType type, MemberName member) {
+ MethodHandle mh = vamh.asCollector(Object[].class, type.parameterCount());
+ mh = mh.asType(type);
+ mh = mh.withInternalMemberName(member);
+ return mh;
}
private static final MethodHandle MH_checkCallerClass;
@@ -939,4 +953,41 @@
}
}
}
+
+
+ /** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */
+ static class WrappedMember extends MethodHandle {
+ private final MethodHandle target;
+ private final MemberName member;
+
+ private WrappedMember(MethodHandle target, MethodType type, MemberName member) {
+ super(type, reinvokerForm(target));
+ this.target = target;
+ this.member = member;
+ }
+
+ @Override
+ MethodHandle reinvokerTarget() {
+ return target;
+ }
+ @Override
+ MemberName internalMemberName() {
+ return member;
+ }
+ @Override
+ boolean isInvokeSpecial() {
+ return target.isInvokeSpecial();
+ }
+ @Override
+ MethodHandle viewAsType(MethodType newType) {
+ return new WrappedMember(target, newType, member);
+ }
+ }
+
+ static MethodHandle makeWrappedMember(MethodHandle target, MemberName member) {
+ if (member.equals(target.internalMemberName()))
+ return target;
+ return new WrappedMember(target, target.type(), member);
+ }
+
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleInfo.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleInfo.java Fri Sep 06 12:10:30 2013 -0400
@@ -24,80 +24,246 @@
*/
package java.lang.invoke;
+
+import java.lang.reflect.*;
+import java.util.*;
import java.lang.invoke.MethodHandleNatives.Constants;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandleStatics.*;
/**
- * Cracking (reflecting) method handles back into their constituent symbolic parts.
+ * A symbolic reference obtained by cracking a method handle into its consitutent symbolic parts.
+ * To crack a direct method handle, call {@link Lookup#revealDirect Lookup.revealDirect}.
+ * <p>
+ * A <em>direct method handle</em> represents a method, constructor, or field without
+ * any intervening argument bindings or other transformations.
+ * The method, constructor, or field referred to by a direct method handle is called
+ * its <em>underlying member</em>.
+ * Direct method handles may be obtained in any of these ways:
+ * <ul>
+ * <li>By executing an {@code ldc} instruction on a {@code CONSTANT_MethodHandle} constant.
+ * (See the Java Virtual Machine Specification, sections 4.4.8 and 5.4.3.)
+ * <li>By calling one of the <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>,
+ * such as {@link Lookup#findVirtual Lookup.findVirtual},
+ * to resolve a symbolic reference into a method handle.
+ * A symbolic reference consists of a class, name string, and type.
+ * <li>By calling the factory method {@link Lookup#unreflect Lookup.unreflect}
+ * or {@link Lookup#unreflectSpecial Lookup.unreflectSpecial}
+ * to convert a {@link Method} into a method handle.
+ * <li>By calling the factory method {@link Lookup#unreflectConstructor Lookup.unreflectConstructor}
+ * to convert a {@link Constructor} into a method handle.
+ * <li>By calling the factory method {@link Lookup#unreflectGetter Lookup.unreflectGetter}
+ * or {@link Lookup#unreflectSetter Lookup.unreflectSetter}
+ * to convert a {@link Field} into a method handle.
+ * </ul>
+ * In all of these cases, it is possible to crack the resulting direct method handle
+ * to recover a symbolic reference for the underlying method, constructor, or field.
+ * Cracking must be done via a {@code Lookup} object equivalent to that which created
+ * the target method handle, or which has enough access permissions to recreate
+ * an equivalent method handle.
*
+ * <h1><a name="refkinds"></a>Reference kinds</h1>
+ * The <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>
+ * correspond to all major use cases for methods, constructors, and fields.
+ * These use cases may be distinguished using small integers as follows:
+ * <table border=1 cellpadding=5 summary="reference kinds">
+ * <tr><th>reference kind</th><th>descriptive name</th><th>scope</th><th>member</th><th>behavior</th></tr>
+ * <tr>
+ * <td>{@code 1}</td><td>{@code REF_getField}</td><td>{@code class}</td>
+ * <td>{@code FT f;}</td><td>{@code (T) this.f;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 2}</td><td>{@code REF_getStatic}</td><td>{@code class} or {@code interface}</td>
+ * <td>{@code static}<br>{@code FT f;}</td><td>{@code (T) C.f;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 3}</td><td>{@code REF_putField}</td><td>{@code class}</td>
+ * <td>{@code FT f;}</td><td>{@code this.f = x;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 4}</td><td>{@code REF_putStatic}</td><td>{@code class}</td>
+ * <td>{@code static}<br>{@code FT f;}</td><td>{@code C.f = arg;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 5}</td><td>{@code REF_invokeVirtual}</td><td>{@code class}</td>
+ * <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 6}</td><td>{@code REF_invokeStatic}</td><td>{@code class} or {@code interface}</td>
+ * <td>{@code static}<br>{@code T m(A*);}</td><td>{@code (T) C.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 7}</td><td>{@code REF_invokeSpecial}</td><td>{@code class} or {@code interface}</td>
+ * <td>{@code T m(A*);}</td><td>{@code (T) super.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 8}</td><td>{@code REF_newInvokeSpecial}</td><td>{@code class}</td>
+ * <td>{@code C(A*);}</td><td>{@code new C(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 9}</td><td>{@code REF_invokeInterface}</td><td>{@code interface}</td>
+ * <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
+ * </tr>
+ * </table>
+ * @since 1.8
*/
-final class MethodHandleInfo {
- public static final int
- REF_getField = Constants.REF_getField,
- REF_getStatic = Constants.REF_getStatic,
- REF_putField = Constants.REF_putField,
- REF_putStatic = Constants.REF_putStatic,
- REF_invokeVirtual = Constants.REF_invokeVirtual,
- REF_invokeStatic = Constants.REF_invokeStatic,
- REF_invokeSpecial = Constants.REF_invokeSpecial,
- REF_newInvokeSpecial = Constants.REF_newInvokeSpecial,
- REF_invokeInterface = Constants.REF_invokeInterface;
-
- private final Class<?> declaringClass;
- private final String name;
- private final MethodType methodType;
- private final int referenceKind;
-
- public MethodHandleInfo(MethodHandle mh) {
- MemberName mn = mh.internalMemberName();
- if (mn == null) throw new IllegalArgumentException("not a direct method handle");
- this.declaringClass = mn.getDeclaringClass();
- this.name = mn.getName();
- this.methodType = mn.getMethodOrFieldType();
- byte refKind = mn.getReferenceKind();
- if (refKind == REF_invokeSpecial && !mh.isInvokeSpecial())
- // Devirtualized method invocation is usually formally virtual.
- refKind = REF_invokeVirtual;
- this.referenceKind = refKind;
- }
+public
+interface MethodHandleInfo {
+ /**
+ * A direct method handle reference kind,
+ * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
+ */
+ public static final int
+ REF_getField = Constants.REF_getField,
+ REF_getStatic = Constants.REF_getStatic,
+ REF_putField = Constants.REF_putField,
+ REF_putStatic = Constants.REF_putStatic,
+ REF_invokeVirtual = Constants.REF_invokeVirtual,
+ REF_invokeStatic = Constants.REF_invokeStatic,
+ REF_invokeSpecial = Constants.REF_invokeSpecial,
+ REF_newInvokeSpecial = Constants.REF_newInvokeSpecial,
+ REF_invokeInterface = Constants.REF_invokeInterface;
- public Class<?> getDeclaringClass() {
- return declaringClass;
- }
+ /**
+ * Returns the reference kind of the cracked method handle, which in turn
+ * determines whether the method handle's underlying member was a constructor, method, or field.
+ * See the <a href="MethodHandleInfo.html#refkinds">table above</a> for definitions.
+ * @return the integer code for the kind of reference used to access the underlying member
+ */
+ public int getReferenceKind();
- public String getName() {
- return name;
- }
+ /**
+ * Returns the class in which the cracked method handle's underlying member was defined.
+ * @return the declaring class of the underlying member
+ */
+ public Class<?> getDeclaringClass();
+
+ /**
+ * Returns the name of the cracked method handle's underlying member.
+ * This is {@code "<init>"} if the underlying member was a constructor,
+ * else it is a simple method name or field name.
+ * @return the simple name of the underlying member
+ */
+ public String getName();
- public MethodType getMethodType() {
- return methodType;
- }
+ /**
+ * Returns the nominal type of the cracked symbolic reference, expressed as a method type.
+ * If the reference is to a constructor, the return type will be {@code void}.
+ * If it is to a non-static method, the method type will not mention the {@code this} parameter.
+ * If it is to a field and the requested access is to read the field,
+ * the method type will have no parameters and return the field type.
+ * If it is to a field and the requested access is to write the field,
+ * the method type will have one parameter of the field type and return {@code void}.
+ * <p>
+ * Note that original direct method handle may include a leading {@code this} parameter,
+ * or (in the case of a constructor) will replace the {@code void} return type
+ * with the constructed class.
+ * The nominal type does not include any {@code this} parameter,
+ * and (in the case of a constructor) will return {@code void}.
+ * @return the type of the underlying member, expressed as a method type
+ */
+ public MethodType getMethodType();
- public int getModifiers() {
- return -1; //TODO
- }
+ // Utility methods.
+ // NOTE: class/name/type and reference kind constitute a symbolic reference
+ // member and modifiers are an add-on, derived from Core Reflection (or the equivalent)
- public int getReferenceKind() {
- return referenceKind;
- }
+ /**
+ * Reflects the underlying member as a method, constructor, or field object.
+ * If the underlying member is public, it is reflected as if by
+ * {@code getMethod}, {@code getConstructor}, or {@code getField}.
+ * Otherwise, it is reflected as if by
+ * {@code getDeclaredMethod}, {@code getDeclaredConstructor}, or {@code getDeclaredField}.
+ * The underlying member must be accessible to the given lookup object.
+ * @param <T> the desired type of the result, either {@link Member} or a subtype
+ * @param expected a class object representing the desired result type {@code T}
+ * @param lookup the lookup object that created this MethodHandleInfo, or one with equivalent access privileges
+ * @return a reference to the method, constructor, or field object
+ * @exception ClassCastException if the member is not of the expected type
+ * @exception NullPointerException if either argument is {@code null}
+ * @exception IllegalArgumentException if the underlying member is not accessible to the given lookup object
+ */
+ public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup);
- static String getReferenceKindString(int referenceKind) {
- switch (referenceKind) {
- case REF_getField: return "getfield";
- case REF_getStatic: return "getstatic";
- case REF_putField: return "putfield";
- case REF_putStatic: return "putstatic";
- case REF_invokeVirtual: return "invokevirtual";
- case REF_invokeStatic: return "invokestatic";
- case REF_invokeSpecial: return "invokespecial";
- case REF_newInvokeSpecial: return "newinvokespecial";
- case REF_invokeInterface: return "invokeinterface";
- default: return "UNKNOWN_REFENCE_KIND" + "[" + referenceKind + "]";
- }
+ /**
+ * Returns the access modifiers of the underlying member.
+ * @return the Java language modifiers for underlying member,
+ * or -1 if the member cannot be accessed
+ * @see Modifier
+ * @see reflectAs
+ */
+ public int getModifiers();
+
+ /**
+ * Determines if the underlying member was a variable arity method or constructor.
+ * Such members are represented by method handles that are varargs collectors.
+ * @implSpec
+ * This produces a result equivalent to:
+ * <pre>{@code
+ * getReferenceKind() >= REF_invokeVirtual && Modifier.isTransient(getModifiers())
+ * }</pre>
+ *
+ *
+ * @return {@code true} if and only if the underlying member was declared with variable arity.
+ */
+ // spelling derived from java.lang.reflect.Executable, not MethodHandle.isVarargsCollector
+ public default boolean isVarArgs() {
+ // fields are never varargs:
+ if (MethodHandleNatives.refKindIsField((byte) getReferenceKind()))
+ return false;
+ // not in the public API: Modifier.VARARGS
+ final int ACC_VARARGS = 0x00000080; // from JVMS 4.6 (Table 4.20)
+ assert(ACC_VARARGS == Modifier.TRANSIENT);
+ return Modifier.isTransient(getModifiers());
}
- @Override
- public String toString() {
- return String.format("%s %s.%s:%s", getReferenceKindString(referenceKind),
- declaringClass.getName(), name, methodType);
+ /**
+ * Returns the descriptive name of the given reference kind,
+ * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
+ * The conventional prefix "REF_" is omitted.
+ * @param referenceKind an integer code for a kind of reference used to access a class member
+ * @return a mixed-case string such as {@code "getField"}
+ * @exception IllegalArgumentException if the argument is not a valid
+ * <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
+ */
+ public static String referenceKindToString(int referenceKind) {
+ if (!MethodHandleNatives.refKindIsValid(referenceKind))
+ throw newIllegalArgumentException("invalid reference kind", referenceKind);
+ return MethodHandleNatives.refKindName((byte)referenceKind);
+ }
+
+ /**
+ * Returns a string representation for a {@code MethodHandleInfo},
+ * given the four parts of its symbolic reference.
+ * This is defined to be of the form {@code "RK C.N:MT"}, where {@code RK} is the
+ * {@linkplain #referenceKindToString reference kind string} for {@code kind},
+ * {@code C} is the {@linkplain java.lang.Class#getName name} of {@code defc}
+ * {@code N} is the {@code name}, and
+ * {@code MT} is the {@code type}.
+ * These four values may be obtained from the
+ * {@linkplain #getReferenceKind reference kind},
+ * {@linkplain #getDeclaringClass declaring class},
+ * {@linkplain #getName member name},
+ * and {@linkplain #getMethodType method type}
+ * of a {@code MethodHandleInfo} object.
+ *
+ * @implSpec
+ * This produces a result equivalent to:
+ * <pre>{@code
+ * String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type)
+ * }</pre>
+ *
+ * @param kind the {@linkplain #getReferenceKind reference kind} part of the symbolic reference
+ * @param defc the {@linkplain #getDeclaringClass declaring class} part of the symbolic reference
+ * @param name the {@linkplain #getName member name} part of the symbolic reference
+ * @param type the {@linkplain #getMethodType method type} part of the symbolic reference
+ * @return a string of the form {@code "RK C.N:MT"}
+ * @exception IllegalArgumentException if the first argument is not a valid
+ * <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
+ * @exception NullPointerException if any reference argument is {@code null}
+ */
+ public static String toString(int kind, Class<?> defc, String name, MethodType type) {
+ Objects.requireNonNull(name); Objects.requireNonNull(type);
+ return String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type);
}
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Fri Sep 06 12:10:30 2013 -0400
@@ -205,6 +205,9 @@
static boolean refKindIsMethod(byte refKind) {
return !refKindIsField(refKind) && (refKind != REF_newInvokeSpecial);
}
+ static boolean refKindIsConstructor(byte refKind) {
+ return (refKind == REF_newInvokeSpecial);
+ }
static boolean refKindHasReceiver(byte refKind) {
assert(refKindIsValid(refKind));
return (refKind & 1) != 0;
@@ -313,7 +316,65 @@
* The method assumes the following arguments on the stack:
* 0: the method handle being invoked
* 1-N: the arguments to the method handle invocation
- * N+1: an implicitly added type argument (the given MethodType)
+ * N+1: an optional, implicitly added argument (typically the given MethodType)
+ * <p>
+ * The nominal method at such a call site is an instance of
+ * a signature-polymorphic method (see @PolymorphicSignature).
+ * Such method instances are user-visible entities which are
+ * "split" from the generic placeholder method in {@code MethodHandle}.
+ * (Note that the placeholder method is not identical with any of
+ * its instances. If invoked reflectively, is guaranteed to throw an
+ * {@code UnsupportedOperationException}.)
+ * If the signature-polymorphic method instance is ever reified,
+ * it appears as a "copy" of the original placeholder
+ * (a native final member of {@code MethodHandle}) except
+ * that its type descriptor has shape required by the instance,
+ * and the method instance is <em>not</em> varargs.
+ * The method instance is also marked synthetic, since the
+ * method (by definition) does not appear in Java source code.
+ * <p>
+ * The JVM is allowed to reify this method as instance metadata.
+ * For example, {@code invokeBasic} is always reified.
+ * But the JVM may instead call {@code linkMethod}.
+ * If the result is an * ordered pair of a {@code (method, appendix)},
+ * the method gets all the arguments (0..N inclusive)
+ * plus the appendix (N+1), and uses the appendix to complete the call.
+ * In this way, one reusable method (called a "linker method")
+ * can perform the function of any number of polymorphic instance
+ * methods.
+ * <p>
+ * Linker methods are allowed to be weakly typed, with any or
+ * all references rewritten to {@code Object} and any primitives
+ * (except {@code long}/{@code float}/{@code double})
+ * rewritten to {@code int}.
+ * A linker method is trusted to return a strongly typed result,
+ * according to the specific method type descriptor of the
+ * signature-polymorphic instance it is emulating.
+ * This can involve (as necessary) a dynamic check using
+ * data extracted from the appendix argument.
+ * <p>
+ * The JVM does not inspect the appendix, other than to pass
+ * it verbatim to the linker method at every call.
+ * This means that the JDK runtime has wide latitude
+ * for choosing the shape of each linker method and its
+ * corresponding appendix.
+ * Linker methods should be generated from {@code LambdaForm}s
+ * so that they do not become visible on stack traces.
+ * <p>
+ * The {@code linkMethod} call is free to omit the appendix
+ * (returning null) and instead emulate the required function
+ * completely in the linker method.
+ * As a corner case, if N==255, no appendix is possible.
+ * In this case, the method returned must be custom-generated to
+ * to perform any needed type checking.
+ * <p>
+ * If the JVM does not reify a method at a call site, but instead
+ * calls {@code linkMethod}, the corresponding call represented
+ * in the bytecodes may mention a valid method which is not
+ * representable with a {@code MemberName}.
+ * Therefore, use cases for {@code linkMethod} tend to correspond to
+ * special cases in reflective code such as {@code findVirtual}
+ * or {@code revealDirect}.
*/
static MemberName linkMethod(Class<?> callerClass, int refKind,
Class<?> defc, String name, Object type,
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Fri Sep 06 12:10:30 2013 -0400
@@ -26,8 +26,6 @@
package java.lang.invoke;
import java.lang.reflect.*;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
@@ -54,6 +52,7 @@
* </ul>
* <p>
* @author John Rose, JSR 292 EG
+ * @since 1.7
*/
public class MethodHandles {
@@ -97,6 +96,38 @@
}
/**
+ * Performs an unchecked "crack" of a direct method handle.
+ * The result is as if the user had obtained a lookup object capable enough
+ * to crack the target method handle, called
+ * {@link java.lang.invoke.MethodHandles.Lookup#revealDirect Lookup.revealDirect}
+ * on the target to obtain its symbolic reference, and then called
+ * {@link java.lang.invoke.MethodHandleInfo#reflectAs MethodHandleInfo.reflectAs}
+ * to resolve the symbolic reference to a member.
+ * <p>
+ * If there is a security manager, its {@code checkPermission} method
+ * is called with a {@code ReflectPermission("suppressAccessChecks")} permission.
+ * @param <T> the desired type of the result, either {@link Member} or a subtype
+ * @param target a direct method handle to crack into symbolic reference components
+ * @param expected a class object representing the desired result type {@code T}
+ * @return a reference to the method, constructor, or field object
+ * @exception SecurityException if the caller is not privileged to call {@code setAccessible}
+ * @exception NullPointerException if either argument is {@code null}
+ * @exception IllegalArgumentException if the target is not a direct method handle
+ * @exception ClassCastException if the member is not of the expected type
+ * @since 1.8
+ */
+ public static <T extends Member> T
+ reflectAs(Class<T> expected, MethodHandle target) {
+ SecurityManager smgr = System.getSecurityManager();
+ if (smgr != null) smgr.checkPermission(ACCESS_PERMISSION);
+ Lookup lookup = Lookup.IMPL_LOOKUP; // use maximally privileged lookup
+ return lookup.revealDirect(target).reflectAs(expected, lookup);
+ }
+ // Copied from AccessibleObject, as used by Method.setAccessible, etc.:
+ static final private java.security.Permission ACCESS_PERMISSION =
+ new ReflectPermission("suppressAccessChecks");
+
+ /**
* A <em>lookup object</em> is a factory for creating method handles,
* when the creation requires access checking.
* Method handles do not perform
@@ -647,6 +678,7 @@
return invoker(type);
if ("invokeExact".equals(name))
return exactInvoker(type);
+ assert(!MemberName.isMethodHandleInvokeName(name));
return null;
}
@@ -892,6 +924,10 @@
* @throws NullPointerException if the argument is null
*/
public MethodHandle unreflect(Method m) throws IllegalAccessException {
+ if (m.getDeclaringClass() == MethodHandle.class) {
+ MethodHandle mh = unreflectForMH(m);
+ if (mh != null) return mh;
+ }
MemberName method = new MemberName(m);
byte refKind = method.getReferenceKind();
if (refKind == REF_invokeSpecial)
@@ -900,6 +936,12 @@
Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this;
return lookup.getDirectMethod(refKind, method.getDeclaringClass(), method, findBoundCallerClass(method));
}
+ private MethodHandle unreflectForMH(Method m) {
+ // these names require special lookups because they throw UnsupportedOperationException
+ if (MemberName.isMethodHandleInvokeName(m.getName()))
+ return MethodHandleImpl.fakeMethodHandleInvoke(new MemberName(m));
+ return null;
+ }
/**
* Produces a method handle for a reflected method.
@@ -1004,6 +1046,46 @@
return unreflectField(f, true);
}
+ /**
+ * Cracks a direct method handle created by this lookup object or a similar one.
+ * Security and access checks are performed to ensure that this lookup object
+ * is capable of reproducing the target method handle.
+ * This means that the cracking may fail if target is a direct method handle
+ * but was created by an unrelated lookup object.
+ * @param target a direct method handle to crack into symbolic reference components
+ * @return a symbolic reference which can be used to reconstruct this method handle from this lookup object
+ * @exception SecurityException if a security manager is present and it
+ * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+ * @throws IllegalArgumentException if the target is not a direct method handle or if access checking fails
+ * @exception NullPointerException if the target is {@code null}
+ * @since 1.8
+ */
+ public MethodHandleInfo revealDirect(MethodHandle target) {
+ MemberName member = target.internalMemberName();
+ if (member == null || (!member.isResolved() && !member.isMethodHandleInvoke()))
+ throw newIllegalArgumentException("not a direct method handle");
+ Class<?> defc = member.getDeclaringClass();
+ byte refKind = member.getReferenceKind();
+ assert(MethodHandleNatives.refKindIsValid(refKind));
+ if (refKind == REF_invokeSpecial && !target.isInvokeSpecial())
+ // Devirtualized method invocation is usually formally virtual.
+ // To avoid creating extra MemberName objects for this common case,
+ // we encode this extra degree of freedom using MH.isInvokeSpecial.
+ refKind = REF_invokeVirtual;
+ if (refKind == REF_invokeVirtual && defc.isInterface())
+ // Symbolic reference is through interface but resolves to Object method (toString, etc.)
+ refKind = REF_invokeInterface;
+ // Check SM permissions and member access before cracking.
+ try {
+ checkSecurityManager(defc, member);
+ checkAccess(refKind, defc, member);
+ } catch (IllegalAccessException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ // Produce the handle to the results.
+ return new InfoFromMemberName(this, member, refKind);
+ }
+
/// Helper methods, all package-private.
MemberName resolveOrFail(byte refKind, Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
@@ -1201,12 +1283,12 @@
private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
boolean doRestrict, Class<?> callerClass) throws IllegalAccessException {
checkMethod(refKind, refc, method);
- if (method.isMethodHandleInvoke())
- return fakeMethodHandleInvoke(method);
+ assert(!method.isMethodHandleInvoke());
Class<?> refcAsSuper;
if (refKind == REF_invokeSpecial &&
refc != lookupClass() &&
+ !refc.isInterface() &&
refc != (refcAsSuper = lookupClass().getSuperclass()) &&
refc.isAssignableFrom(lookupClass())) {
assert(!method.getName().equals("<init>")); // not this code path
@@ -1234,9 +1316,6 @@
mh = restrictReceiver(method, mh, lookupClass());
return mh;
}
- private MethodHandle fakeMethodHandleInvoke(MemberName method) {
- return throwException(method.getReturnType(), UnsupportedOperationException.class);
- }
private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
Class<?> callerClass)
throws IllegalAccessException {
--- a/jdk/src/share/classes/java/lang/invoke/SerializedLambda.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/lang/invoke/SerializedLambda.java Fri Sep 06 12:10:30 2013 -0400
@@ -225,7 +225,7 @@
@Override
public String toString() {
- String implKind=MethodHandleInfo.getReferenceKindString(implMethodKind);
+ String implKind=MethodHandleInfo.referenceKindToString(implMethodKind);
return String.format("SerializedLambda[%s=%s, %s=%s.%s:%s, " +
"%s=%s %s.%s:%s, %s=%s, %s=%d]",
"capturingClass", capturingClass,
--- a/jdk/src/share/classes/java/net/IDN.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/net/IDN.java Fri Sep 06 12:10:30 2013 -0400
@@ -292,13 +292,17 @@
if (useSTD3ASCIIRules) {
for (int i = 0; i < dest.length(); i++) {
int c = dest.charAt(i);
- if (!isLDHChar(c)) {
- throw new IllegalArgumentException("Contains non-LDH characters");
+ if (isNonLDHAsciiCodePoint(c)) {
+ throw new IllegalArgumentException(
+ "Contains non-LDH ASCII characters");
}
}
- if (dest.charAt(0) == '-' || dest.charAt(dest.length() - 1) == '-') {
- throw new IllegalArgumentException("Has leading or trailing hyphen");
+ if (dest.charAt(0) == '-' ||
+ dest.charAt(dest.length() - 1) == '-') {
+
+ throw new IllegalArgumentException(
+ "Has leading or trailing hyphen");
}
}
@@ -401,26 +405,20 @@
//
// LDH stands for "letter/digit/hyphen", with characters restricted to the
// 26-letter Latin alphabet <A-Z a-z>, the digits <0-9>, and the hyphen
- // <->
- // non-LDH = 0..0x2C, 0x2E..0x2F, 0x3A..0x40, 0x56..0x60, 0x7B..0x7F
+ // <->.
+ // Non LDH refers to characters in the ASCII range, but which are not
+ // letters, digits or the hypen.
+ //
+ // non-LDH = 0..0x2C, 0x2E..0x2F, 0x3A..0x40, 0x5B..0x60, 0x7B..0x7F
//
- private static boolean isLDHChar(int ch){
- // high runner case
- if(ch > 0x007A){
- return false;
- }
- //['-' '0'..'9' 'A'..'Z' 'a'..'z']
- if((ch == 0x002D) ||
- (0x0030 <= ch && ch <= 0x0039) ||
- (0x0041 <= ch && ch <= 0x005A) ||
- (0x0061 <= ch && ch <= 0x007A)
- ){
- return true;
- }
- return false;
+ private static boolean isNonLDHAsciiCodePoint(int ch){
+ return (0x0000 <= ch && ch <= 0x002C) ||
+ (0x002E <= ch && ch <= 0x002F) ||
+ (0x003A <= ch && ch <= 0x0040) ||
+ (0x005B <= ch && ch <= 0x0060) ||
+ (0x007B <= ch && ch <= 0x007F);
}
-
//
// search dots in a string and return the index of that character;
// or if there is no dots, return the length of input string
--- a/jdk/src/share/classes/java/nio/file/Files.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/nio/file/Files.java Fri Sep 06 12:10:30 2013 -0400
@@ -25,34 +25,56 @@
package java.nio.file;
-import java.nio.file.attribute.*;
-import java.nio.file.spi.FileSystemProvider;
-import java.nio.file.spi.FileTypeDetector;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.UncheckedIOException;
+import java.io.Writer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
-import java.io.Closeable;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.io.Writer;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.util.*;
-import java.util.function.BiPredicate;
-import java.util.stream.CloseableStream;
-import java.util.stream.DelegatingStream;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.DosFileAttributes;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.FileOwnerAttributeView;
+import java.nio.file.attribute.FileStoreAttributeView;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.UserPrincipal;
+import java.nio.file.spi.FileSystemProvider;
+import java.nio.file.spi.FileTypeDetector;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.BiPredicate;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
/**
* This class consists exclusively of static methods that operate on files,
@@ -74,6 +96,21 @@
return path.getFileSystem().provider();
}
+ /**
+ * Convert a Closeable to a Runnable by converting checked IOException
+ * to UncheckedIOException
+ */
+ private static Runnable asUncheckedRunnable(Closeable c) {
+ return () -> {
+ try {
+ c.close();
+ }
+ catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ };
+ }
+
// -- File contents --
/**
@@ -3228,29 +3265,7 @@
// -- Stream APIs --
/**
- * Implementation of CloseableStream
- */
- private static class DelegatingCloseableStream<T> extends DelegatingStream<T>
- implements CloseableStream<T>
- {
- private final Closeable closeable;
-
- DelegatingCloseableStream(Closeable c, Stream<T> delegate) {
- super(delegate);
- this.closeable = c;
- }
-
- public void close() {
- try {
- closeable.close();
- } catch (IOException ex) {
- throw new UncheckedIOException(ex);
- }
- }
- }
-
- /**
- * Return a lazily populated {@code CloseableStream}, the elements of
+ * Return a lazily populated {@code Stream}, the elements of
* which are the entries in the directory. The listing is not recursive.
*
* <p> The elements of the stream are {@link Path} objects that are
@@ -3264,10 +3279,13 @@
* reflect updates to the directory that occur after returning from this
* method.
*
- * <p> When not using the try-with-resources construct, then the stream's
- * {@link CloseableStream#close close} method should be invoked after the
- * operation is completed so as to free any resources held for the open
- * directory. Operating on a closed stream behaves as if the end of stream
+ * <p> The returned stream encapsulates a {@link DirectoryStream}.
+ * If timely disposal of file system resources is required, the
+ * {@code try}-with-resources construct should be used to ensure that the
+ * stream's {@link Stream#close close} method is invoked after the stream
+ * operations are completed.
+ *
+ * <p> Operating on a closed stream behaves as if the end of stream
* has been reached. Due to read-ahead, one or more elements may be
* returned after the stream has been closed.
*
@@ -3278,7 +3296,7 @@
*
* @param dir The path to the directory
*
- * @return The {@code CloseableStream} describing the content of the
+ * @return The {@code Stream} describing the content of the
* directory
*
* @throws NotDirectoryException
@@ -3294,43 +3312,54 @@
* @see #newDirectoryStream(Path)
* @since 1.8
*/
- public static CloseableStream<Path> list(Path dir) throws IOException {
+ public static Stream<Path> list(Path dir) throws IOException {
DirectoryStream<Path> ds = Files.newDirectoryStream(dir);
- final Iterator<Path> delegate = ds.iterator();
+ try {
+ final Iterator<Path> delegate = ds.iterator();
- // Re-wrap DirectoryIteratorException to UncheckedIOException
- Iterator<Path> it = new Iterator<Path>() {
- public boolean hasNext() {
+ // Re-wrap DirectoryIteratorException to UncheckedIOException
+ Iterator<Path> it = new Iterator<Path>() {
+ @Override
+ public boolean hasNext() {
+ try {
+ return delegate.hasNext();
+ } catch (DirectoryIteratorException e) {
+ throw new UncheckedIOException(e.getCause());
+ }
+ }
+ @Override
+ public Path next() {
+ try {
+ return delegate.next();
+ } catch (DirectoryIteratorException e) {
+ throw new UncheckedIOException(e.getCause());
+ }
+ }
+ };
+
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false)
+ .onClose(asUncheckedRunnable(ds));
+ } catch (Error|RuntimeException e) {
+ try {
+ ds.close();
+ } catch (IOException ex) {
try {
- return delegate.hasNext();
- } catch (DirectoryIteratorException e) {
- throw new UncheckedIOException(e.getCause());
- }
+ e.addSuppressed(ex);
+ } catch (Throwable ignore) {}
}
- public Path next() {
- try {
- return delegate.next();
- } catch (DirectoryIteratorException e) {
- throw new UncheckedIOException(e.getCause());
- }
- }
- };
-
- Stream<Path> s = StreamSupport.stream(
- Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT),
- false);
- return new DelegatingCloseableStream<>(ds, s);
+ throw e;
+ }
}
/**
- * Return a {@code CloseableStream} that is lazily populated with {@code
+ * Return a {@code Stream} that is lazily populated with {@code
* Path} by walking the file tree rooted at a given starting file. The
* file tree is traversed <em>depth-first</em>, the elements in the stream
* are {@link Path} objects that are obtained as if by {@link
* Path#resolve(Path) resolving} the relative path against {@code start}.
*
* <p> The {@code stream} walks the file tree as elements are consumed.
- * The {@code CloseableStream} returned is guaranteed to have at least one
+ * The {@code Stream} returned is guaranteed to have at least one
* element, the starting file itself. For each file visited, the stream
* attempts to read its {@link BasicFileAttributes}. If the file is a
* directory and can be opened successfully, entries in the directory, and
@@ -3370,10 +3399,11 @@
* <p> When a security manager is installed and it denies access to a file
* (or directory), then it is ignored and not included in the stream.
*
- * <p> When not using the try-with-resources construct, then the stream's
- * {@link CloseableStream#close close} method should be invoked after the
- * operation is completed so as to free any resources held for the open
- * directory. Operate the stream after it is closed will throw an
+ * <p> The returned stream encapsulates one or more {@link DirectoryStream}s.
+ * If timely disposal of file system resources is required, the
+ * {@code try}-with-resources construct should be used to ensure that the
+ * stream's {@link Stream#close close} method is invoked after the stream
+ * operations are completed. Operating on a closed stream will result in an
* {@link java.lang.IllegalStateException}.
*
* <p> If an {@link IOException} is thrown when accessing the directory
@@ -3388,7 +3418,7 @@
* @param options
* options to configure the traversal
*
- * @return the {@link CloseableStream} of {@link Path}
+ * @return the {@link Stream} of {@link Path}
*
* @throws IllegalArgumentException
* if the {@code maxDepth} parameter is negative
@@ -3401,21 +3431,22 @@
* if an I/O error is thrown when accessing the starting file.
* @since 1.8
*/
- public static CloseableStream<Path> walk(Path start, int maxDepth,
- FileVisitOption... options)
- throws IOException
- {
+ public static Stream<Path> walk(Path start, int maxDepth,
+ FileVisitOption... options)
+ throws IOException {
FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options);
-
- Stream<Path> s = StreamSupport.stream(
- Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT),
- false).
- map(entry -> entry.file());
- return new DelegatingCloseableStream<>(iterator, s);
+ try {
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), false)
+ .onClose(iterator::close)
+ .map(entry -> entry.file());
+ } catch (Error|RuntimeException e) {
+ iterator.close();
+ throw e;
+ }
}
/**
- * Return a {@code CloseableStream} that is lazily populated with {@code
+ * Return a {@code Stream} that is lazily populated with {@code
* Path} by walking the file tree rooted at a given starting file. The
* file tree is traversed <em>depth-first</em>, the elements in the stream
* are {@link Path} objects that are obtained as if by {@link
@@ -3428,12 +3459,19 @@
* </pre></blockquote>
* In other words, it visits all levels of the file tree.
*
+ * <p> The returned stream encapsulates one or more {@link DirectoryStream}s.
+ * If timely disposal of file system resources is required, the
+ * {@code try}-with-resources construct should be used to ensure that the
+ * stream's {@link Stream#close close} method is invoked after the stream
+ * operations are completed. Operating on a closed stream will result in an
+ * {@link java.lang.IllegalStateException}.
+ *
* @param start
* the starting file
* @param options
* options to configure the traversal
*
- * @return the {@link CloseableStream} of {@link Path}
+ * @return the {@link Stream} of {@link Path}
*
* @throws SecurityException
* If the security manager denies access to the starting file.
@@ -3446,15 +3484,14 @@
* @see #walk(Path, int, FileVisitOption...)
* @since 1.8
*/
- public static CloseableStream<Path> walk(Path start,
- FileVisitOption... options)
- throws IOException
- {
+ public static Stream<Path> walk(Path start,
+ FileVisitOption... options)
+ throws IOException {
return walk(start, Integer.MAX_VALUE, options);
}
/**
- * Return a {@code CloseableStream} that is lazily populated with {@code
+ * Return a {@code Stream} that is lazily populated with {@code
* Path} by searching for files in a file tree rooted at a given starting
* file.
*
@@ -3463,12 +3500,19 @@
* {@link BiPredicate} is invoked with its {@link Path} and {@link
* BasicFileAttributes}. The {@code Path} object is obtained as if by
* {@link Path#resolve(Path) resolving} the relative path against {@code
- * start} and is only included in the returned {@link CloseableStream} if
+ * start} and is only included in the returned {@link Stream} if
* the {@code BiPredicate} returns true. Compare to calling {@link
* java.util.stream.Stream#filter filter} on the {@code Stream}
* returned by {@code walk} method, this method may be more efficient by
* avoiding redundant retrieval of the {@code BasicFileAttributes}.
*
+ * <p> The returned stream encapsulates one or more {@link DirectoryStream}s.
+ * If timely disposal of file system resources is required, the
+ * {@code try}-with-resources construct should be used to ensure that the
+ * stream's {@link Stream#close close} method is invoked after the stream
+ * operations are completed. Operating on a closed stream will result in an
+ * {@link java.lang.IllegalStateException}.
+ *
* <p> If an {@link IOException} is thrown when accessing the directory
* after returned from this method, it is wrapped in an {@link
* UncheckedIOException} which will be thrown from the method that caused
@@ -3484,7 +3528,7 @@
* @param options
* options to configure the traversal
*
- * @return the {@link CloseableStream} of {@link Path}
+ * @return the {@link Stream} of {@link Path}
*
* @throws IllegalArgumentException
* if the {@code maxDepth} parameter is negative
@@ -3499,24 +3543,25 @@
* @see #walk(Path, int, FileVisitOption...)
* @since 1.8
*/
- public static CloseableStream<Path> find(Path start,
- int maxDepth,
- BiPredicate<Path, BasicFileAttributes> matcher,
- FileVisitOption... options)
- throws IOException
- {
+ public static Stream<Path> find(Path start,
+ int maxDepth,
+ BiPredicate<Path, BasicFileAttributes> matcher,
+ FileVisitOption... options)
+ throws IOException {
FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options);
-
- Stream<Path> s = StreamSupport.stream(
- Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT),
- false).
- filter(entry -> matcher.test(entry.file(), entry.attributes())).
- map(entry -> entry.file());
- return new DelegatingCloseableStream<>(iterator, s);
+ try {
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), false)
+ .onClose(iterator::close)
+ .filter(entry -> matcher.test(entry.file(), entry.attributes()))
+ .map(entry -> entry.file());
+ } catch (Error|RuntimeException e) {
+ iterator.close();
+ throw e;
+ }
}
/**
- * Read all lines from a file as a {@code CloseableStream}. Unlike {@link
+ * Read all lines from a file as a {@code Stream}. Unlike {@link
* #readAllLines(Path, Charset) readAllLines}, this method does not read
* all lines into a {@code List}, but instead populates lazily as the stream
* is consumed.
@@ -3528,22 +3573,24 @@
* <p> After this method returns, then any subsequent I/O exception that
* occurs while reading from the file or when a malformed or unmappable byte
* sequence is read, is wrapped in an {@link UncheckedIOException} that will
- * be thrown form the
+ * be thrown from the
* {@link java.util.stream.Stream} method that caused the read to take
* place. In case an {@code IOException} is thrown when closing the file,
* it is also wrapped as an {@code UncheckedIOException}.
*
- * <p> When not using the try-with-resources construct, then stream's
- * {@link CloseableStream#close close} method should be invoked after
- * operation is completed so as to free any resources held for the open
- * file.
+ * <p> The returned stream encapsulates a {@link Reader}. If timely
+ * disposal of file system resources is required, the try-with-resources
+ * construct should be used to ensure that the stream's
+ * {@link Stream#close close} method is invoked after the stream operations
+ * are completed.
+ *
*
* @param path
* the path to the file
* @param cs
* the charset to use for decoding
*
- * @return the lines from the file as a {@code CloseableStream}
+ * @return the lines from the file as a {@code Stream}
*
* @throws IOException
* if an I/O error occurs opening the file
@@ -3557,10 +3604,19 @@
* @see java.io.BufferedReader#lines()
* @since 1.8
*/
- public static CloseableStream<String> lines(Path path, Charset cs)
- throws IOException
- {
+ public static Stream<String> lines(Path path, Charset cs) throws IOException {
BufferedReader br = Files.newBufferedReader(path, cs);
- return new DelegatingCloseableStream<>(br, br.lines());
+ try {
+ return br.lines().onClose(asUncheckedRunnable(br));
+ } catch (Error|RuntimeException e) {
+ try {
+ br.close();
+ } catch (IOException ex) {
+ try {
+ e.addSuppressed(ex);
+ } catch (Throwable ignore) {}
+ }
+ throw e;
+ }
}
}
--- a/jdk/src/share/classes/java/util/HashMap.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/HashMap.java Fri Sep 06 12:10:30 2013 -0400
@@ -25,13 +25,14 @@
package java.util;
-import java.io.*;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
-import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
-import java.util.function.BiFunction;
import java.util.function.Function;
/**
@@ -63,20 +64,25 @@
* structures are rebuilt) so that the hash table has approximately twice the
* number of buckets.
*
- * <p>As a general rule, the default load factor (.75) offers a good tradeoff
- * between time and space costs. Higher values decrease the space overhead
- * but increase the lookup cost (reflected in most of the operations of the
- * <tt>HashMap</tt> class, including <tt>get</tt> and <tt>put</tt>). The
- * expected number of entries in the map and its load factor should be taken
- * into account when setting its initial capacity, so as to minimize the
- * number of rehash operations. If the initial capacity is greater
- * than the maximum number of entries divided by the load factor, no
- * rehash operations will ever occur.
+ * <p>As a general rule, the default load factor (.75) offers a good
+ * tradeoff between time and space costs. Higher values decrease the
+ * space overhead but increase the lookup cost (reflected in most of
+ * the operations of the <tt>HashMap</tt> class, including
+ * <tt>get</tt> and <tt>put</tt>). The expected number of entries in
+ * the map and its load factor should be taken into account when
+ * setting its initial capacity, so as to minimize the number of
+ * rehash operations. If the initial capacity is greater than the
+ * maximum number of entries divided by the load factor, no rehash
+ * operations will ever occur.
*
- * <p>If many mappings are to be stored in a <tt>HashMap</tt> instance,
- * creating it with a sufficiently large capacity will allow the mappings to
- * be stored more efficiently than letting it perform automatic rehashing as
- * needed to grow the table.
+ * <p>If many mappings are to be stored in a <tt>HashMap</tt>
+ * instance, creating it with a sufficiently large capacity will allow
+ * the mappings to be stored more efficiently than letting it perform
+ * automatic rehashing as needed to grow the table. Note that using
+ * many keys with the same {@code hashCode()} is a sure way to slow
+ * down performance of any hash table. To ameliorate impact, when keys
+ * are {@link Comparable}, this class may use comparison order among
+ * keys to help break ties.
*
* <p><strong>Note that this implementation is not synchronized.</strong>
* If multiple threads access a hash map concurrently, and at least one of
@@ -128,11 +134,100 @@
* @see Hashtable
* @since 1.2
*/
+public class HashMap<K,V> extends AbstractMap<K,V>
+ implements Map<K,V>, Cloneable, Serializable {
-public class HashMap<K,V>
- extends AbstractMap<K,V>
- implements Map<K,V>, Cloneable, Serializable
-{
+ private static final long serialVersionUID = 362498820763181265L;
+
+ /*
+ * Implementation notes.
+ *
+ * This map usually acts as a binned (bucketed) hash table, but
+ * when bins get too large, they are transformed into bins of
+ * TreeNodes, each structured similarly to those in
+ * java.util.TreeMap. Most methods try to use normal bins, but
+ * relay to TreeNode methods when applicable (simply by checking
+ * instanceof a node). Bins of TreeNodes may be traversed and
+ * used like any others, but additionally support faster lookup
+ * when overpopulated. However, since the vast majority of bins in
+ * normal use are not overpopulated, checking for existence of
+ * tree bins may be delayed in the course of table methods.
+ *
+ * Tree bins (i.e., bins whose elements are all TreeNodes) are
+ * ordered primarily by hashCode, but in the case of ties, if two
+ * elements are of the same "class C implements Comparable<C>",
+ * type then their compareTo method is used for ordering. (We
+ * conservatively check generic types via reflection to validate
+ * this -- see method comparableClassFor). The added complexity
+ * of tree bins is worthwhile in providing worst-case O(log n)
+ * operations when keys either have distinct hashes or are
+ * orderable, Thus, performance degrades gracefully under
+ * accidental or malicious usages in which hashCode() methods
+ * return values that are poorly distributed, as well as those in
+ * which many keys share a hashCode, so long as they are also
+ * Comparable. (If neither of these apply, we may waste about a
+ * factor of two in time and space compared to taking no
+ * precautions. But the only known cases stem from poor user
+ * programming practices that are already so slow that this makes
+ * little difference.)
+ *
+ * Because TreeNodes are about twice the size of regular nodes, we
+ * use them only when bins contain enough nodes to warrant use
+ * (see TREEIFY_THRESHOLD). And when they become too small (due to
+ * removal or resizing) they are converted back to plain bins. In
+ * usages with well-distributed user hashCodes, tree bins are
+ * rarely used. Ideally, under random hashCodes, the frequency of
+ * nodes in bins follows a Poisson distribution
+ * (http://en.wikipedia.org/wiki/Poisson_distribution) with a
+ * parameter of about 0.5 on average for the default resizing
+ * threshold of 0.75, although with a large variance because of
+ * resizing granularity. Ignoring variance, the expected
+ * occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
+ * factorial(k)). The first values are:
+ *
+ * 0: 0.60653066
+ * 1: 0.30326533
+ * 2: 0.07581633
+ * 3: 0.01263606
+ * 4: 0.00157952
+ * 5: 0.00015795
+ * 6: 0.00001316
+ * 7: 0.00000094
+ * 8: 0.00000006
+ * more: less than 1 in ten million
+ *
+ * The root of a tree bin is normally its first node. However,
+ * sometimes (currently only upon Iterator.remove), the root might
+ * be elsewhere, but can be recovered following parent links
+ * (method TreeNode.root()).
+ *
+ * All applicable internal methods accept a hash code as an
+ * argument (as normally supplied from a public method), allowing
+ * them to call each other without recomputing user hashCodes.
+ * Most internal methods also accept a "tab" argument, that is
+ * normally the current table, but may be a new or old one when
+ * resizing or converting.
+ *
+ * When bin lists are treeified, split, or untreeified, we keep
+ * them in the same relative access/traversal order (i.e., field
+ * Node.next) to better preserve locality, and to slightly
+ * simplify handling of splits and traversals that invoke
+ * iterator.remove. When using comparators on insertion, to keep a
+ * total ordering (or as close as is required here) across
+ * rebalancings, we compare classes and identityHashCodes as
+ * tie-breakers.
+ *
+ * The use and transitions among plain vs tree modes is
+ * complicated by the existence of subclass LinkedHashMap. See
+ * below for hook methods defined to be invoked upon insertion,
+ * removal and access that allow LinkedHashMap internals to
+ * otherwise remain independent of these mechanics. (This also
+ * requires that a map instance be passed to some utility methods
+ * that may create new nodes.)
+ *
+ * The concurrent-programming-like SSA-based coding style helps
+ * avoid aliasing errors amid all of the twisty pointer operations.
+ */
/**
* The default initial capacity - MUST be a power of two.
@@ -152,14 +247,158 @@
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
- * An empty table instance to share when the table is not inflated.
+ * The bin count threshold for using a tree rather than list for a
+ * bin. Bins are converted to trees when adding an element to a
+ * bin with at least this many nodes. The value must be greater
+ * than 2 and should be at least 8 to mesh with assumptions in
+ * tree removal about conversion back to plain bins upon
+ * shrinkage.
+ */
+ static final int TREEIFY_THRESHOLD = 8;
+
+ /**
+ * The bin count threshold for untreeifying a (split) bin during a
+ * resize operation. Should be less than TREEIFY_THRESHOLD, and at
+ * most 6 to mesh with shrinkage detection under removal.
+ */
+ static final int UNTREEIFY_THRESHOLD = 6;
+
+ /**
+ * The smallest table capacity for which bins may be treeified.
+ * (Otherwise the table is resized if too many nodes in a bin.)
+ * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
+ * between resizing and treeification thresholds.
+ */
+ static final int MIN_TREEIFY_CAPACITY = 64;
+
+ /**
+ * Basic hash bin node, used for most entries. (See below for
+ * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
*/
- static final Object[] EMPTY_TABLE = {};
+ static class Node<K,V> implements Map.Entry<K,V> {
+ final int hash;
+ final K key;
+ V value;
+ Node<K,V> next;
+
+ Node(int hash, K key, V value, Node<K,V> next) {
+ this.hash = hash;
+ this.key = key;
+ this.value = value;
+ this.next = next;
+ }
+
+ public final K getKey() { return key; }
+ public final V getValue() { return value; }
+ public final String toString() { return key + "=" + value; }
+
+ public final int hashCode() {
+ return Objects.hashCode(key) ^ Objects.hashCode(value);
+ }
+
+ public final V setValue(V newValue) {
+ V oldValue = value;
+ value = newValue;
+ return oldValue;
+ }
+
+ public final boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (o instanceof Map.Entry) {
+ Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+ if (Objects.equals(key, e.getKey()) &&
+ Objects.equals(value, e.getValue()))
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /* ---------------- Static utilities -------------- */
/**
- * The table, resized as necessary. Length MUST Always be a power of two.
+ * Computes key.hashCode() and spreads (XORs) higher bits of hash
+ * to lower. Because the table uses power-of-two masking, sets of
+ * hashes that vary only in bits above the current mask will
+ * always collide. (Among known examples are sets of Float keys
+ * holding consecutive whole numbers in small tables.) So we
+ * apply a transform that spreads the impact of higher bits
+ * downward. There is a tradeoff between speed, utility, and
+ * quality of bit-spreading. Because many common sets of hashes
+ * are already reasonably distributed (so don't benefit from
+ * spreading), and because we use trees to handle large sets of
+ * collisions in bins, we just XOR some shifted bits in the
+ * cheapest possible way to reduce systematic lossage, as well as
+ * to incorporate impact of the highest bits that would otherwise
+ * never be used in index calculations because of table bounds.
+ */
+ static final int hash(Object key) {
+ int h;
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+ }
+
+ /**
+ * Returns x's Class if it is of the form "class C implements
+ * Comparable<C>", else null.
*/
- transient Object[] table = EMPTY_TABLE;
+ static Class<?> comparableClassFor(Object x) {
+ if (x instanceof Comparable) {
+ Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
+ if ((c = x.getClass()) == String.class) // bypass checks
+ return c;
+ if ((ts = c.getGenericInterfaces()) != null) {
+ for (int i = 0; i < ts.length; ++i) {
+ if (((t = ts[i]) instanceof ParameterizedType) &&
+ ((p = (ParameterizedType)t).getRawType() ==
+ Comparable.class) &&
+ (as = p.getActualTypeArguments()) != null &&
+ as.length == 1 && as[0] == c) // type arg is c
+ return c;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns k.compareTo(x) if x matches kc (k's screened comparable
+ * class), else 0.
+ */
+ @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
+ static int compareComparables(Class<?> kc, Object k, Object x) {
+ return (x == null || x.getClass() != kc ? 0 :
+ ((Comparable)k).compareTo(x));
+ }
+
+ /**
+ * Returns a power of two size for the given target capacity.
+ */
+ static final int tableSizeFor(int cap) {
+ int n = cap - 1;
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+
+ /* ---------------- Fields -------------- */
+
+ /**
+ * The table, initialized on first use, and resized as
+ * necessary. When allocated, length is always a power of two.
+ * (We also tolerate length zero in some operations to allow
+ * bootstrapping mechanics that are currently not needed.)
+ */
+ transient Node<K,V>[] table;
+
+ /**
+ * Holds cached entrySet(). Note that AbstractMap fields are used
+ * for keySet() and values().
+ */
+ transient Set<Map.Entry<K,V>> entrySet;
/**
* The number of key-value mappings contained in this map.
@@ -167,21 +406,6 @@
transient int size;
/**
- * The next size value at which to resize (capacity * load factor).
- * @serial
- */
- // If table == EMPTY_TABLE then this is the initial capacity at which the
- // table will be created when inflated.
- int threshold;
-
- /**
- * The load factor for the hash table.
- *
- * @serial
- */
- final float loadFactor;
-
- /**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
@@ -191,627 +415,24 @@
transient int modCount;
/**
- * Holds values which can't be initialized until after VM is booted.
+ * The next size value at which to resize (capacity * load factor).
+ *
+ * @serial
*/
- private static class Holder {
- static final sun.misc.Unsafe UNSAFE;
-
- /**
- * Offset of "final" hashSeed field we must set in
- * readObject() method.
- */
- static final long HASHSEED_OFFSET;
-
- static final boolean USE_HASHSEED;
-
- static {
- String hashSeedProp = java.security.AccessController.doPrivileged(
- new sun.security.action.GetPropertyAction(
- "jdk.map.useRandomSeed"));
- boolean localBool = (null != hashSeedProp)
- ? Boolean.parseBoolean(hashSeedProp) : false;
- USE_HASHSEED = localBool;
-
- if (USE_HASHSEED) {
- try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- HASHSEED_OFFSET = UNSAFE.objectFieldOffset(
- HashMap.class.getDeclaredField("hashSeed"));
- } catch (NoSuchFieldException | SecurityException e) {
- throw new InternalError("Failed to record hashSeed offset", e);
- }
- } else {
- UNSAFE = null;
- HASHSEED_OFFSET = 0;
- }
- }
- }
-
- /*
- * A randomizing value associated with this instance that is applied to
- * hash code of keys to make hash collisions harder to find.
- *
- * Non-final so it can be set lazily, but be sure not to set more than once.
- */
- transient final int hashSeed;
-
- /*
- * TreeBin/TreeNode code from CHM doesn't handle the null key. Store the
- * null key entry here.
- */
- transient Entry<K,V> nullKeyEntry = null;
-
- /*
- * In order to improve performance under high hash-collision conditions,
- * HashMap will switch to storing a bin's entries in a balanced tree
- * (TreeBin) instead of a linked-list once the number of entries in the bin
- * passes a certain threshold (TreeBin.TREE_THRESHOLD), if at least one of
- * the keys in the bin implements Comparable. This technique is borrowed
- * from ConcurrentHashMap.
- */
-
- /*
- * Code based on CHMv8
- *
- * Node type for TreeBin
- */
- final static class TreeNode<K,V> {
- TreeNode parent; // red-black tree links
- TreeNode left;
- TreeNode right;
- TreeNode prev; // needed to unlink next upon deletion
- boolean red;
- final HashMap.Entry<K,V> entry;
-
- TreeNode(HashMap.Entry<K,V> entry, Object next, TreeNode parent) {
- this.entry = entry;
- this.entry.next = next;
- this.parent = parent;
- }
- }
+ // (The javadoc description is true upon serialization.
+ // Additionally, if the table array has not been allocated, this
+ // field holds the initial array capacity, or zero signifying
+ // DEFAULT_INITIAL_CAPACITY.)
+ int threshold;
/**
- * Returns a Class for the given object of the form "class C
- * implements Comparable<C>", if one exists, else null. See the TreeBin
- * docs, below, for explanation.
- */
- static Class<?> comparableClassFor(Object x) {
- Class<?> c, s, cmpc; Type[] ts, as; Type t; ParameterizedType p;
- if ((c = x.getClass()) == String.class) // bypass checks
- return c;
- if ((cmpc = Comparable.class).isAssignableFrom(c)) {
- while (cmpc.isAssignableFrom(s = c.getSuperclass()))
- c = s; // find topmost comparable class
- if ((ts = c.getGenericInterfaces()) != null) {
- for (int i = 0; i < ts.length; ++i) {
- if (((t = ts[i]) instanceof ParameterizedType) &&
- ((p = (ParameterizedType)t).getRawType() == cmpc) &&
- (as = p.getActualTypeArguments()) != null &&
- as.length == 1 && as[0] == c) // type arg is c
- return c;
- }
- }
- }
- return null;
- }
-
- /*
- * Code based on CHMv8
+ * The load factor for the hash table.
*
- * A specialized form of red-black tree for use in bins
- * whose size exceeds a threshold.
- *
- * TreeBins use a special form of comparison for search and
- * related operations (which is the main reason we cannot use
- * existing collections such as TreeMaps). TreeBins contain
- * Comparable elements, but may contain others, as well as
- * elements that are Comparable but not necessarily Comparable<T>
- * for the same T, so we cannot invoke compareTo among them. To
- * handle this, the tree is ordered primarily by hash value, then
- * by Comparable.compareTo order if applicable. On lookup at a
- * node, if elements are not comparable or compare as 0 then both
- * left and right children may need to be searched in the case of
- * tied hash values. (This corresponds to the full list search
- * that would be necessary if all elements were non-Comparable and
- * had tied hashes.) The red-black balancing code is updated from
- * pre-jdk-collections
- * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java)
- * based in turn on Cormen, Leiserson, and Rivest "Introduction to
- * Algorithms" (CLR).
+ * @serial
*/
- final class TreeBin {
- /*
- * The bin count threshold for using a tree rather than list for a bin. The
- * value reflects the approximate break-even point for using tree-based
- * operations.
- */
- static final int TREE_THRESHOLD = 16;
-
- TreeNode<K,V> root; // root of tree
- TreeNode<K,V> first; // head of next-pointer list
-
- /*
- * Split a TreeBin into lo and hi parts and install in given table.
- *
- * Existing Entrys are re-used, which maintains the before/after links for
- * LinkedHashMap.Entry.
- *
- * No check for Comparable, though this is the same as CHM.
- */
- final void splitTreeBin(Object[] newTable, int i, TreeBin loTree, TreeBin hiTree) {
- TreeBin oldTree = this;
- int bit = newTable.length >>> 1;
- int loCount = 0, hiCount = 0;
- TreeNode<K,V> e = oldTree.first;
- TreeNode<K,V> next;
-
- // This method is called when the table has just increased capacity,
- // so indexFor() is now taking one additional bit of hash into
- // account ("bit"). Entries in this TreeBin now belong in one of
- // two bins, "i" or "i+bit", depending on if the new top bit of the
- // hash is set. The trees for the two bins are loTree and hiTree.
- // If either tree ends up containing fewer than TREE_THRESHOLD
- // entries, it is converted back to a linked list.
- while (e != null) {
- // Save entry.next - it will get overwritten in putTreeNode()
- next = (TreeNode<K,V>)e.entry.next;
-
- int h = e.entry.hash;
- K k = (K) e.entry.key;
- V v = e.entry.value;
- if ((h & bit) == 0) {
- ++loCount;
- // Re-using e.entry
- loTree.putTreeNode(h, k, v, e.entry);
- } else {
- ++hiCount;
- hiTree.putTreeNode(h, k, v, e.entry);
- }
- // Iterate using the saved 'next'
- e = next;
- }
- if (loCount < TREE_THRESHOLD) { // too small, convert back to list
- HashMap.Entry loEntry = null;
- TreeNode<K,V> p = loTree.first;
- while (p != null) {
- @SuppressWarnings("unchecked")
- TreeNode<K,V> savedNext = (TreeNode<K,V>) p.entry.next;
- p.entry.next = loEntry;
- loEntry = p.entry;
- p = savedNext;
- }
- // assert newTable[i] == null;
- newTable[i] = loEntry;
- } else {
- // assert newTable[i] == null;
- newTable[i] = loTree;
- }
- if (hiCount < TREE_THRESHOLD) { // too small, convert back to list
- HashMap.Entry hiEntry = null;
- TreeNode<K,V> p = hiTree.first;
- while (p != null) {
- @SuppressWarnings("unchecked")
- TreeNode<K,V> savedNext = (TreeNode<K,V>) p.entry.next;
- p.entry.next = hiEntry;
- hiEntry = p.entry;
- p = savedNext;
- }
- // assert newTable[i + bit] == null;
- newTable[i + bit] = hiEntry;
- } else {
- // assert newTable[i + bit] == null;
- newTable[i + bit] = hiTree;
- }
- }
-
- /*
- * Popuplate the TreeBin with entries from the linked list e
- *
- * Assumes 'this' is a new/empty TreeBin
- *
- * Note: no check for Comparable
- * Note: I believe this changes iteration order
- */
- @SuppressWarnings("unchecked")
- void populate(HashMap.Entry e) {
- // assert root == null;
- // assert first == null;
- HashMap.Entry next;
- while (e != null) {
- // Save entry.next - it will get overwritten in putTreeNode()
- next = (HashMap.Entry)e.next;
- // Re-using Entry e will maintain before/after in LinkedHM
- putTreeNode(e.hash, (K)e.key, (V)e.value, e);
- // Iterate using the saved 'next'
- e = next;
- }
- }
-
- /**
- * Copied from CHMv8
- * From CLR
- */
- private void rotateLeft(TreeNode p) {
- if (p != null) {
- TreeNode r = p.right, pp, rl;
- if ((rl = p.right = r.left) != null) {
- rl.parent = p;
- }
- if ((pp = r.parent = p.parent) == null) {
- root = r;
- } else if (pp.left == p) {
- pp.left = r;
- } else {
- pp.right = r;
- }
- r.left = p;
- p.parent = r;
- }
- }
-
- /**
- * Copied from CHMv8
- * From CLR
- */
- private void rotateRight(TreeNode p) {
- if (p != null) {
- TreeNode l = p.left, pp, lr;
- if ((lr = p.left = l.right) != null) {
- lr.parent = p;
- }
- if ((pp = l.parent = p.parent) == null) {
- root = l;
- } else if (pp.right == p) {
- pp.right = l;
- } else {
- pp.left = l;
- }
- l.right = p;
- p.parent = l;
- }
- }
-
- /**
- * Returns the TreeNode (or null if not found) for the given
- * key. A front-end for recursive version.
- */
- final TreeNode getTreeNode(int h, K k) {
- return getTreeNode(h, k, root, comparableClassFor(k));
- }
-
- /**
- * Returns the TreeNode (or null if not found) for the given key
- * starting at given root.
- */
- @SuppressWarnings("unchecked")
- final TreeNode getTreeNode (int h, K k, TreeNode p, Class<?> cc) {
- // assert k != null;
- while (p != null) {
- int dir, ph; Object pk;
- if ((ph = p.entry.hash) != h)
- dir = (h < ph) ? -1 : 1;
- else if ((pk = p.entry.key) == k || k.equals(pk))
- return p;
- else if (cc == null || comparableClassFor(pk) != cc ||
- (dir = ((Comparable<Object>)k).compareTo(pk)) == 0) {
- // assert pk != null;
- TreeNode r, pl, pr; // check both sides
- if ((pr = p.right) != null &&
- (r = getTreeNode(h, k, pr, cc)) != null)
- return r;
- else if ((pl = p.left) != null)
- dir = -1;
- else // nothing there
- break;
- }
- p = (dir > 0) ? p.right : p.left;
- }
- return null;
- }
+ final float loadFactor;
- /*
- * Finds or adds a node.
- *
- * 'entry' should be used to recycle an existing Entry (e.g. in the case
- * of converting a linked-list bin to a TreeBin).
- * If entry is null, a new Entry will be created for the new TreeNode
- *
- * @return the TreeNode containing the mapping, or null if a new
- * TreeNode was added
- */
- @SuppressWarnings("unchecked")
- TreeNode putTreeNode(int h, K k, V v, HashMap.Entry<K,V> entry) {
- // assert k != null;
- //if (entry != null) {
- // assert h == entry.hash;
- // assert k == entry.key;
- // assert v == entry.value;
- // }
- Class<?> cc = comparableClassFor(k);
- TreeNode pp = root, p = null;
- int dir = 0;
- while (pp != null) { // find existing node or leaf to insert at
- int ph; Object pk;
- p = pp;
- if ((ph = p.entry.hash) != h)
- dir = (h < ph) ? -1 : 1;
- else if ((pk = p.entry.key) == k || k.equals(pk))
- return p;
- else if (cc == null || comparableClassFor(pk) != cc ||
- (dir = ((Comparable<Object>)k).compareTo(pk)) == 0) {
- TreeNode r, pr;
- if ((pr = p.right) != null &&
- (r = getTreeNode(h, k, pr, cc)) != null)
- return r;
- else // continue left
- dir = -1;
- }
- pp = (dir > 0) ? p.right : p.left;
- }
-
- // Didn't find the mapping in the tree, so add it
- TreeNode f = first;
- TreeNode x;
- if (entry != null) {
- x = new TreeNode(entry, f, p);
- } else {
- x = new TreeNode(newEntry(h, k, v, null), f, p);
- }
- first = x;
-
- if (p == null) {
- root = x;
- } else { // attach and rebalance; adapted from CLR
- TreeNode xp, xpp;
- if (f != null) {
- f.prev = x;
- }
- if (dir <= 0) {
- p.left = x;
- } else {
- p.right = x;
- }
- x.red = true;
- while (x != null && (xp = x.parent) != null && xp.red
- && (xpp = xp.parent) != null) {
- TreeNode xppl = xpp.left;
- if (xp == xppl) {
- TreeNode y = xpp.right;
- if (y != null && y.red) {
- y.red = false;
- xp.red = false;
- xpp.red = true;
- x = xpp;
- } else {
- if (x == xp.right) {
- rotateLeft(x = xp);
- xpp = (xp = x.parent) == null ? null : xp.parent;
- }
- if (xp != null) {
- xp.red = false;
- if (xpp != null) {
- xpp.red = true;
- rotateRight(xpp);
- }
- }
- }
- } else {
- TreeNode y = xppl;
- if (y != null && y.red) {
- y.red = false;
- xp.red = false;
- xpp.red = true;
- x = xpp;
- } else {
- if (x == xp.left) {
- rotateRight(x = xp);
- xpp = (xp = x.parent) == null ? null : xp.parent;
- }
- if (xp != null) {
- xp.red = false;
- if (xpp != null) {
- xpp.red = true;
- rotateLeft(xpp);
- }
- }
- }
- }
- }
- TreeNode r = root;
- if (r != null && r.red) {
- r.red = false;
- }
- }
- return null;
- }
-
- /*
- * From CHMv8
- *
- * Removes the given node, that must be present before this
- * call. This is messier than typical red-black deletion code
- * because we cannot swap the contents of an interior node
- * with a leaf successor that is pinned by "next" pointers
- * that are accessible independently of lock. So instead we
- * swap the tree linkages.
- */
- final void deleteTreeNode(TreeNode p) {
- TreeNode next = (TreeNode) p.entry.next; // unlink traversal pointers
- TreeNode pred = p.prev;
- if (pred == null) {
- first = next;
- } else {
- pred.entry.next = next;
- }
- if (next != null) {
- next.prev = pred;
- }
- TreeNode replacement;
- TreeNode pl = p.left;
- TreeNode pr = p.right;
- if (pl != null && pr != null) {
- TreeNode s = pr, sl;
- while ((sl = s.left) != null) // find successor
- {
- s = sl;
- }
- boolean c = s.red;
- s.red = p.red;
- p.red = c; // swap colors
- TreeNode sr = s.right;
- TreeNode pp = p.parent;
- if (s == pr) { // p was s's direct parent
- p.parent = s;
- s.right = p;
- } else {
- TreeNode sp = s.parent;
- if ((p.parent = sp) != null) {
- if (s == sp.left) {
- sp.left = p;
- } else {
- sp.right = p;
- }
- }
- if ((s.right = pr) != null) {
- pr.parent = s;
- }
- }
- p.left = null;
- if ((p.right = sr) != null) {
- sr.parent = p;
- }
- if ((s.left = pl) != null) {
- pl.parent = s;
- }
- if ((s.parent = pp) == null) {
- root = s;
- } else if (p == pp.left) {
- pp.left = s;
- } else {
- pp.right = s;
- }
- replacement = sr;
- } else {
- replacement = (pl != null) ? pl : pr;
- }
- TreeNode pp = p.parent;
- if (replacement == null) {
- if (pp == null) {
- root = null;
- return;
- }
- replacement = p;
- } else {
- replacement.parent = pp;
- if (pp == null) {
- root = replacement;
- } else if (p == pp.left) {
- pp.left = replacement;
- } else {
- pp.right = replacement;
- }
- p.left = p.right = p.parent = null;
- }
- if (!p.red) { // rebalance, from CLR
- TreeNode x = replacement;
- while (x != null) {
- TreeNode xp, xpl;
- if (x.red || (xp = x.parent) == null) {
- x.red = false;
- break;
- }
- if (x == (xpl = xp.left)) {
- TreeNode sib = xp.right;
- if (sib != null && sib.red) {
- sib.red = false;
- xp.red = true;
- rotateLeft(xp);
- sib = (xp = x.parent) == null ? null : xp.right;
- }
- if (sib == null) {
- x = xp;
- } else {
- TreeNode sl = sib.left, sr = sib.right;
- if ((sr == null || !sr.red)
- && (sl == null || !sl.red)) {
- sib.red = true;
- x = xp;
- } else {
- if (sr == null || !sr.red) {
- if (sl != null) {
- sl.red = false;
- }
- sib.red = true;
- rotateRight(sib);
- sib = (xp = x.parent) == null ?
- null : xp.right;
- }
- if (sib != null) {
- sib.red = (xp == null) ? false : xp.red;
- if ((sr = sib.right) != null) {
- sr.red = false;
- }
- }
- if (xp != null) {
- xp.red = false;
- rotateLeft(xp);
- }
- x = root;
- }
- }
- } else { // symmetric
- TreeNode sib = xpl;
- if (sib != null && sib.red) {
- sib.red = false;
- xp.red = true;
- rotateRight(xp);
- sib = (xp = x.parent) == null ? null : xp.left;
- }
- if (sib == null) {
- x = xp;
- } else {
- TreeNode sl = sib.left, sr = sib.right;
- if ((sl == null || !sl.red)
- && (sr == null || !sr.red)) {
- sib.red = true;
- x = xp;
- } else {
- if (sl == null || !sl.red) {
- if (sr != null) {
- sr.red = false;
- }
- sib.red = true;
- rotateLeft(sib);
- sib = (xp = x.parent) == null ?
- null : xp.left;
- }
- if (sib != null) {
- sib.red = (xp == null) ? false : xp.red;
- if ((sl = sib.left) != null) {
- sl.red = false;
- }
- }
- if (xp != null) {
- xp.red = false;
- rotateRight(xp);
- }
- x = root;
- }
- }
- }
- }
- }
- if (p == replacement && (pp = p.parent) != null) {
- if (p == pp.left) // detach pointers
- {
- pp.left = null;
- } else if (p == pp.right) {
- pp.right = null;
- }
- p.parent = null;
- }
- }
- }
+ /* ---------------- Public operations -------------- */
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
@@ -832,9 +453,7 @@
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
- threshold = initialCapacity;
- hashSeed = initHashSeed();
- init();
+ this.threshold = tableSizeFor(initialCapacity);
}
/**
@@ -853,7 +472,7 @@
* (16) and the default load factor (0.75).
*/
public HashMap() {
- this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
+ this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
/**
@@ -866,79 +485,35 @@
* @throws NullPointerException if the specified map is null
*/
public HashMap(Map<? extends K, ? extends V> m) {
- this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
- DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
- inflateTable(threshold);
-
- putAllForCreate(m);
- // assert size == m.size();
- }
-
- private static int roundUpToPowerOf2(int number) {
- // assert number >= 0 : "number must be non-negative";
- return number >= MAXIMUM_CAPACITY
- ? MAXIMUM_CAPACITY
- : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
+ this.loadFactor = DEFAULT_LOAD_FACTOR;
+ putMapEntries(m, false);
}
/**
- * Inflates the table.
- */
- private void inflateTable(int toSize) {
- // Find a power of 2 >= toSize
- int capacity = roundUpToPowerOf2(toSize);
-
- threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
- table = new Object[capacity];
- }
-
- // internal utilities
-
- /**
- * Initialization hook for subclasses. This method is called
- * in all constructors and pseudo-constructors (clone, readObject)
- * after HashMap has been initialized but before any entries have
- * been inserted. (In the absence of this method, readObject would
- * require explicit knowledge of subclasses.)
- */
- void init() {
- }
-
- /**
- * Return an initial value for the hashSeed, or 0 if the random seed is not
- * enabled.
+ * Implements Map.putAll and Map constructor
+ *
+ * @param m the map
+ * @param evict false when initially constructing this map, else
+ * true (relayed to method afterNodeInsertion).
*/
- final int initHashSeed() {
- if (sun.misc.VM.isBooted() && Holder.USE_HASHSEED) {
- int seed = ThreadLocalRandom.current().nextInt();
- return (seed != 0) ? seed : 1;
+ final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
+ int s = m.size();
+ if (s > 0) {
+ if (table == null) { // pre-size
+ float ft = ((float)s / loadFactor) + 1.0F;
+ int t = ((ft < (float)MAXIMUM_CAPACITY) ?
+ (int)ft : MAXIMUM_CAPACITY);
+ if (t > threshold)
+ threshold = tableSizeFor(t);
+ }
+ else if (s > threshold)
+ resize();
+ for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
+ K key = e.getKey();
+ V value = e.getValue();
+ putVal(hash(key), key, value, false, evict);
+ }
}
- return 0;
- }
-
- /**
- * Retrieve object hash code and applies a supplemental hash function to the
- * result hash, which defends against poor quality hash functions. This is
- * critical because HashMap uses power-of-two length hash tables, that
- * otherwise encounter collisions for hashCodes that do not differ
- * in lower bits.
- */
- final int hash(Object k) {
- int h = hashSeed ^ k.hashCode();
-
- // This function ensures that hashCodes that differ only by
- // constant multiples at each bit position have a bounded
- // number of collisions (approximately 8 at default load factor).
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
- }
-
- /**
- * Returns index for hash code h.
- */
- static int indexFor(int h, int length) {
- // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
- return h & (length-1);
}
/**
@@ -976,18 +551,36 @@
*
* @see #put(Object, Object)
*/
- @SuppressWarnings("unchecked")
public V get(Object key) {
- Entry<K,V> entry = getEntry(key);
-
- return null == entry ? null : entry.getValue();
+ Node<K,V> e;
+ return (e = getNode(hash(key), key)) == null ? null : e.value;
}
- @Override
- public V getOrDefault(Object key, V defaultValue) {
- Entry<K,V> entry = getEntry(key);
-
- return (entry == null) ? defaultValue : entry.getValue();
+ /**
+ * Implements Map.get and related methods
+ *
+ * @param hash hash for key
+ * @param key the key
+ * @return the node, or null if none
+ */
+ final Node<K,V> getNode(int hash, Object key) {
+ Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (first = tab[(n - 1) & hash]) != null) {
+ if (first.hash == hash && // always check first node
+ ((k = first.key) == key || (key != null && key.equals(k))))
+ return first;
+ if ((e = first.next) != null) {
+ if (first instanceof TreeNode)
+ return ((TreeNode<K,V>)first).getTreeNode(hash, key);
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ return e;
+ } while ((e = e.next) != null);
+ }
+ }
+ return null;
}
/**
@@ -999,49 +592,10 @@
* key.
*/
public boolean containsKey(Object key) {
- return getEntry(key) != null;
+ return getNode(hash(key), key) != null;
}
/**
- * Returns the entry associated with the specified key in the
- * HashMap. Returns null if the HashMap contains no mapping
- * for the key.
- */
- @SuppressWarnings("unchecked")
- final Entry<K,V> getEntry(Object key) {
- if (size == 0) {
- return null;
- }
- if (key == null) {
- return nullKeyEntry;
- }
- int hash = hash(key);
- int bin = indexFor(hash, table.length);
-
- if (table[bin] instanceof Entry) {
- Entry<K,V> e = (Entry<K,V>) table[bin];
- for (; e != null; e = (Entry<K,V>)e.next) {
- Object k;
- if (e.hash == hash &&
- ((k = e.key) == key || key.equals(k))) {
- return e;
- }
- }
- } else if (table[bin] != null) {
- TreeBin e = (TreeBin)table[bin];
- TreeNode p = e.getTreeNode(hash, (K)key);
- if (p != null) {
- // assert p.entry.hash == hash && p.entry.key.equals(key);
- return (Entry<K,V>)p.entry;
- } else {
- return null;
- }
- }
- return null;
- }
-
-
- /**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
@@ -1053,202 +607,169 @@
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
- @SuppressWarnings("unchecked")
public V put(K key, V value) {
- if (table == EMPTY_TABLE) {
- inflateTable(threshold);
- }
- if (key == null)
- return putForNullKey(value);
- int hash = hash(key);
- int i = indexFor(hash, table.length);
- boolean checkIfNeedTree = false; // Might we convert bin to a TreeBin?
+ return putVal(hash(key), key, value, false, true);
+ }
- if (table[i] instanceof Entry) {
- // Bin contains ordinary Entries. Search for key in the linked list
- // of entries, counting the number of entries. Only check for
- // TreeBin conversion if the list size is >= TREE_THRESHOLD.
- // (The conversion still may not happen if the table gets resized.)
- int listSize = 0;
- Entry<K,V> e = (Entry<K,V>) table[i];
- for (; e != null; e = (Entry<K,V>)e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- V oldValue = e.value;
+ /**
+ * Implements Map.put and related methods
+ *
+ * @param hash hash for key
+ * @param key the key
+ * @param value the value to put
+ * @param onlyIfAbsent if true, don't change existing value
+ * @param evict if false, the table is in creation mode.
+ * @return previous value, or null if none
+ */
+ final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ Node<K,V>[] tab; Node<K,V> p; int n, i;
+ if (size > threshold || (tab = table) == null ||
+ (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+ else {
+ Node<K,V> e; K k;
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ e = p;
+ else if (p instanceof TreeNode)
+ e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
+ else {
+ for (int binCount = 0; ; ++binCount) {
+ if ((e = p.next) == null) {
+ p.next = newNode(hash, key, value, null);
+ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);
+ break;
+ }
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ break;
+ p = e;
+ }
+ }
+ if (e != null) { // existing mapping for key
+ V oldValue = e.value;
+ if (!onlyIfAbsent || oldValue == null)
e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- listSize++;
- }
- // Didn't find, so fall through and call addEntry() to add the
- // Entry and check for TreeBin conversion.
- checkIfNeedTree = listSize >= TreeBin.TREE_THRESHOLD;
- } else if (table[i] != null) {
- TreeBin e = (TreeBin)table[i];
- TreeNode p = e.putTreeNode(hash, key, value, null);
- if (p == null) { // putTreeNode() added a new node
- modCount++;
- size++;
- if (size >= threshold) {
- resize(2 * table.length);
- }
- return null;
- } else { // putTreeNode() found an existing node
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- V oldVal = pEntry.value;
- pEntry.value = value;
- pEntry.recordAccess(this);
- return oldVal;
+ afterNodeAccess(e);
+ return oldValue;
}
}
- modCount++;
- addEntry(hash, key, value, i, checkIfNeedTree);
+ ++modCount;
+ ++size;
+ afterNodeInsertion(evict);
return null;
}
/**
- * Offloaded version of put for null keys
+ * Initializes or doubles table size. If null, allocates in
+ * accord with initial capacity target held in field threshold.
+ * Otherwise, because we are using power-of-two expansion, the
+ * elements from each bin must either stay at same index, or move
+ * with a power of two offset in the new table.
+ *
+ * @return the table
*/
- private V putForNullKey(V value) {
- if (nullKeyEntry != null) {
- V oldValue = nullKeyEntry.value;
- nullKeyEntry.value = value;
- nullKeyEntry.recordAccess(this);
- return oldValue;
+ final Node<K,V>[] resize() {
+ Node<K,V>[] oldTab = table;
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap > 0) {
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ newThr = oldThr << 1; // double threshold
}
- modCount++;
- size++; // newEntry() skips size++
- nullKeyEntry = newEntry(0, null, value, null);
- return null;
- }
-
- private void putForCreateNullKey(V value) {
- // Look for preexisting entry for key. This will never happen for
- // clone or deserialize. It will only happen for construction if the
- // input Map is a sorted map whose ordering is inconsistent w/ equals.
- if (nullKeyEntry != null) {
- nullKeyEntry.value = value;
- } else {
- nullKeyEntry = newEntry(0, null, value, null);
- size++;
+ else if (oldThr > 0) // initial capacity was placed in threshold
+ newCap = oldThr;
+ else { // zero initial threshold signifies using defaults
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
- }
-
-
- /**
- * This method is used instead of put by constructors and
- * pseudoconstructors (clone, readObject). It does not resize the table,
- * check for comodification, etc, though it will convert bins to TreeBins
- * as needed. It calls createEntry rather than addEntry.
- */
- @SuppressWarnings("unchecked")
- private void putForCreate(K key, V value) {
- if (null == key) {
- putForCreateNullKey(value);
- return;
+ if (newThr == 0) {
+ float ft = (float)newCap * loadFactor;
+ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
}
- int hash = hash(key);
- int i = indexFor(hash, table.length);
- boolean checkIfNeedTree = false; // Might we convert bin to a TreeBin?
-
- /**
- * Look for preexisting entry for key. This will never happen for
- * clone or deserialize. It will only happen for construction if the
- * input Map is a sorted map whose ordering is inconsistent w/ equals.
- */
- if (table[i] instanceof Entry) {
- int listSize = 0;
- Entry<K,V> e = (Entry<K,V>) table[i];
- for (; e != null; e = (Entry<K,V>)e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- e.value = value;
- return;
+ threshold = newThr;
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
+ table = newTab;
+ if (oldTab != null) {
+ for (int j = 0; j < oldCap; ++j) {
+ Node<K,V> e;
+ if ((e = oldTab[j]) != null) {
+ oldTab[j] = null;
+ if (e.next == null)
+ newTab[e.hash & (newCap - 1)] = e;
+ else if (e instanceof TreeNode)
+ ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
+ else { // preserve order
+ Node<K,V> loHead = null, loTail = null;
+ Node<K,V> hiHead = null, hiTail = null;
+ Node<K,V> next;
+ do {
+ next = e.next;
+ if ((e.hash & oldCap) == 0) {
+ if (loTail == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ }
+ else {
+ if (hiTail == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ }
+ } while ((e = next) != null);
+ if (loTail != null) {
+ loTail.next = null;
+ newTab[j] = loHead;
+ }
+ if (hiTail != null) {
+ hiTail.next = null;
+ newTab[j + oldCap] = hiHead;
+ }
+ }
}
- listSize++;
}
- // Didn't find, fall through to createEntry().
- // Check for conversion to TreeBin done via createEntry().
- checkIfNeedTree = listSize >= TreeBin.TREE_THRESHOLD;
- } else if (table[i] != null) {
- TreeBin e = (TreeBin)table[i];
- TreeNode p = e.putTreeNode(hash, key, value, null);
- if (p != null) {
- p.entry.setValue(value); // Found an existing node, set value
- } else {
- size++; // Added a new TreeNode, so update size
- }
- // don't need modCount++/check for resize - just return
- return;
}
-
- createEntry(hash, key, value, i, checkIfNeedTree);
- }
-
- private void putAllForCreate(Map<? extends K, ? extends V> m) {
- for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
- putForCreate(e.getKey(), e.getValue());
+ return newTab;
}
/**
- * Rehashes the contents of this map into a new array with a
- * larger capacity. This method is called automatically when the
- * number of keys in this map reaches its threshold.
- *
- * If current capacity is MAXIMUM_CAPACITY, this method does not
- * resize the map, but sets threshold to Integer.MAX_VALUE.
- * This has the effect of preventing future calls.
- *
- * @param newCapacity the new capacity, MUST be a power of two;
- * must be greater than current capacity unless current
- * capacity is MAXIMUM_CAPACITY (in which case value
- * is irrelevant).
+ * Replaces all linked nodes in bin at index for given hash unless
+ * table is too small, in which case resizes instead.
*/
- void resize(int newCapacity) {
- Object[] oldTable = table;
- int oldCapacity = oldTable.length;
- if (oldCapacity == MAXIMUM_CAPACITY) {
- threshold = Integer.MAX_VALUE;
- return;
+ final void treeifyBin(Node<K,V>[] tab, int hash) {
+ int n, index; Node<K,V> e;
+ if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
+ resize();
+ else if ((e = tab[index = (n - 1) & hash]) != null) {
+ TreeNode<K,V> hd = null, tl = null;
+ do {
+ TreeNode<K,V> p = replacementTreeNode(e, null);
+ if (tl == null)
+ hd = p;
+ else {
+ p.prev = tl;
+ tl.next = p;
+ }
+ tl = p;
+ } while ((e = e.next) != null);
+ if ((tab[index] = hd) != null)
+ hd.treeify(tab);
}
-
- Object[] newTable = new Object[newCapacity];
- transfer(newTable);
- table = newTable;
- threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
- }
-
- /**
- * Transfers all entries from current table to newTable.
- *
- * Assumes newTable is larger than table
- */
- @SuppressWarnings("unchecked")
- void transfer(Object[] newTable) {
- Object[] src = table;
- // assert newTable.length > src.length : "newTable.length(" +
- // newTable.length + ") expected to be > src.length("+src.length+")";
- int newCapacity = newTable.length;
- for (int j = 0; j < src.length; j++) {
- if (src[j] instanceof Entry) {
- // Assume: since wasn't TreeBin before, won't need TreeBin now
- Entry<K,V> e = (Entry<K,V>) src[j];
- while (null != e) {
- Entry<K,V> next = (Entry<K,V>)e.next;
- int i = indexFor(e.hash, newCapacity);
- e.next = (Entry<K,V>) newTable[i];
- newTable[i] = e;
- e = next;
- }
- } else if (src[j] != null) {
- TreeBin e = (TreeBin) src[j];
- TreeBin loTree = new TreeBin();
- TreeBin hiTree = new TreeBin();
- e.splitTreeBin(newTable, j, loTree, hiTree);
- }
- }
- Arrays.fill(table, null);
}
/**
@@ -1260,30 +781,8 @@
* @throws NullPointerException if the specified map is null
*/
public void putAll(Map<? extends K, ? extends V> m) {
- int numKeysToBeAdded = m.size();
- if (numKeysToBeAdded == 0)
- return;
-
- if (table == EMPTY_TABLE) {
- inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
- }
-
- /*
- * Expand the map if the map if the number of mappings to be added
- * is greater than or equal to threshold. This is conservative; the
- * obvious condition is (m.size() + size) >= threshold, but this
- * condition could result in a map with twice the appropriate capacity,
- * if the keys to be added overlap with the keys already in this map.
- * By using the conservative calculation, we subject ourself
- * to at most one extra resize.
- */
- if (numKeysToBeAdded > threshold && table.length < MAXIMUM_CAPACITY) {
- resize(table.length * 2);
- }
-
- for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
- put(e.getKey(), e.getValue());
- }
+ putMapEntries(m, true);
+ }
/**
* Removes the mapping for the specified key from this map if present.
@@ -1295,834 +794,74 @@
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V remove(Object key) {
- Entry<K,V> e = removeEntryForKey(key);
- return (e == null ? null : e.value);
- }
-
- // optimized implementations of default methods in Map
-
- @Override
- public void forEach(BiConsumer<? super K, ? super V> action) {
- Objects.requireNonNull(action);
- final int expectedModCount = modCount;
- if (nullKeyEntry != null) {
- forEachNullKey(expectedModCount, action);
- }
- Object[] tab = this.table;
- for (int index = 0; index < tab.length; index++) {
- Object item = tab[index];
- if (item == null) {
- continue;
- }
- if (item instanceof HashMap.TreeBin) {
- eachTreeNode(expectedModCount, ((TreeBin)item).first, action);
- continue;
- }
- @SuppressWarnings("unchecked")
- Entry<K, V> entry = (Entry<K, V>)item;
- while (entry != null) {
- action.accept(entry.key, entry.value);
- entry = (Entry<K, V>)entry.next;
-
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
- }
- }
-
- private void eachTreeNode(int expectedModCount, TreeNode<K, V> node, BiConsumer<? super K, ? super V> action) {
- while (node != null) {
- @SuppressWarnings("unchecked")
- Entry<K, V> entry = (Entry<K, V>)node.entry;
- action.accept(entry.key, entry.value);
- node = (TreeNode<K, V>)entry.next;
-
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
- }
-
- private void forEachNullKey(int expectedModCount, BiConsumer<? super K, ? super V> action) {
- action.accept(null, nullKeyEntry.value);
-
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
-
- @Override
- public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
- Objects.requireNonNull(function);
- final int expectedModCount = modCount;
- if (nullKeyEntry != null) {
- replaceforNullKey(expectedModCount, function);
- }
- Object[] tab = this.table;
- for (int index = 0; index < tab.length; index++) {
- Object item = tab[index];
- if (item == null) {
- continue;
- }
- if (item instanceof HashMap.TreeBin) {
- replaceEachTreeNode(expectedModCount, ((TreeBin)item).first, function);
- continue;
- }
- @SuppressWarnings("unchecked")
- Entry<K, V> entry = (Entry<K, V>)item;
- while (entry != null) {
- entry.value = function.apply(entry.key, entry.value);
- entry = (Entry<K, V>)entry.next;
-
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
- }
- }
-
- private void replaceEachTreeNode(int expectedModCount, TreeNode<K, V> node, BiFunction<? super K, ? super V, ? extends V> function) {
- while (node != null) {
- @SuppressWarnings("unchecked")
- Entry<K, V> entry = (Entry<K, V>)node.entry;
- entry.value = function.apply(entry.key, entry.value);
- node = (TreeNode<K, V>)entry.next;
-
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
- }
-
- private void replaceforNullKey(int expectedModCount, BiFunction<? super K, ? super V, ? extends V> function) {
- nullKeyEntry.value = function.apply(null, nullKeyEntry.value);
-
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
+ Node<K,V> e;
+ return (e = removeNode(hash(key), key, null, false, true)) == null ?
+ null : e.value;
}
- @Override
- public V putIfAbsent(K key, V value) {
- if (table == EMPTY_TABLE) {
- inflateTable(threshold);
- }
- if (key == null) {
- if (nullKeyEntry == null || nullKeyEntry.value == null) {
- putForNullKey(value);
- return null;
- } else {
- return nullKeyEntry.value;
- }
- }
- int hash = hash(key);
- int i = indexFor(hash, table.length);
- boolean checkIfNeedTree = false; // Might we convert bin to a TreeBin?
-
- if (table[i] instanceof Entry) {
- int listSize = 0;
- Entry<K,V> e = (Entry<K,V>) table[i];
- for (; e != null; e = (Entry<K,V>)e.next) {
- if (e.hash == hash && Objects.equals(e.key, key)) {
- if (e.value != null) {
- return e.value;
- }
- e.value = value;
- e.recordAccess(this);
- return null;
- }
- listSize++;
- }
- // Didn't find, so fall through and call addEntry() to add the
- // Entry and check for TreeBin conversion.
- checkIfNeedTree = listSize >= TreeBin.TREE_THRESHOLD;
- } else if (table[i] != null) {
- TreeBin e = (TreeBin)table[i];
- TreeNode p = e.putTreeNode(hash, key, value, null);
- if (p == null) { // not found, putTreeNode() added a new node
- modCount++;
- size++;
- if (size >= threshold) {
- resize(2 * table.length);
- }
- return null;
- } else { // putTreeNode() found an existing node
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- V oldVal = pEntry.value;
- if (oldVal == null) { // only replace if maps to null
- pEntry.value = value;
- pEntry.recordAccess(this);
- }
- return oldVal;
- }
- }
- modCount++;
- addEntry(hash, key, value, i, checkIfNeedTree);
- return null;
- }
-
- @Override
- public boolean remove(Object key, Object value) {
- if (size == 0) {
- return false;
- }
- if (key == null) {
- if (nullKeyEntry != null &&
- Objects.equals(nullKeyEntry.value, value)) {
- removeNullKey();
- return true;
- }
- return false;
- }
- int hash = hash(key);
- int i = indexFor(hash, table.length);
-
- if (table[i] instanceof Entry) {
- @SuppressWarnings("unchecked")
- Entry<K,V> prev = (Entry<K,V>) table[i];
- Entry<K,V> e = prev;
- while (e != null) {
- @SuppressWarnings("unchecked")
- Entry<K,V> next = (Entry<K,V>) e.next;
- if (e.hash == hash && Objects.equals(e.key, key)) {
- if (!Objects.equals(e.value, value)) {
- return false;
- }
- modCount++;
- size--;
- if (prev == e)
- table[i] = next;
- else
- prev.next = next;
- e.recordRemoval(this);
- return true;
- }
- prev = e;
- e = next;
- }
- } else if (table[i] != null) {
- TreeBin tb = ((TreeBin) table[i]);
- TreeNode p = tb.getTreeNode(hash, (K)key);
- if (p != null) {
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- // assert pEntry.key.equals(key);
- if (Objects.equals(pEntry.value, value)) {
- modCount++;
- size--;
- tb.deleteTreeNode(p);
- pEntry.recordRemoval(this);
- if (tb.root == null || tb.first == null) {
- // assert tb.root == null && tb.first == null :
- // "TreeBin.first and root should both be null";
- // TreeBin is now empty, we should blank this bin
- table[i] = null;
- }
- return true;
+ /**
+ * Implements Map.remove and related methods
+ *
+ * @param hash hash for key
+ * @param key the key
+ * @param value the value to match if matchValue, else ignored
+ * @param matchValue if true only remove if value is equal
+ * @param movable if false do not move other nodes while removing
+ * @return the node, or null if none
+ */
+ final Node<K,V> removeNode(int hash, Object key, Object value,
+ boolean matchValue, boolean movable) {
+ Node<K,V>[] tab; Node<K,V> p; int n, index;
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (p = tab[index = (n - 1) & hash]) != null) {
+ Node<K,V> node = null, e; K k; V v;
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ node = p;
+ else if ((e = p.next) != null) {
+ if (p instanceof TreeNode)
+ node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
+ else {
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key ||
+ (key != null && key.equals(k)))) {
+ node = e;
+ break;
+ }
+ p = e;
+ } while ((e = e.next) != null);
}
}
- }
- return false;
- }
-
- @Override
- public boolean replace(K key, V oldValue, V newValue) {
- if (size == 0) {
- return false;
- }
- if (key == null) {
- if (nullKeyEntry != null &&
- Objects.equals(nullKeyEntry.value, oldValue)) {
- putForNullKey(newValue);
- return true;
- }
- return false;
- }
- int hash = hash(key);
- int i = indexFor(hash, table.length);
-
- if (table[i] instanceof Entry) {
- @SuppressWarnings("unchecked")
- Entry<K,V> e = (Entry<K,V>) table[i];
- for (; e != null; e = (Entry<K,V>)e.next) {
- if (e.hash == hash && Objects.equals(e.key, key) && Objects.equals(e.value, oldValue)) {
- e.value = newValue;
- e.recordAccess(this);
- return true;
- }
- }
- return false;
- } else if (table[i] != null) {
- TreeBin tb = ((TreeBin) table[i]);
- TreeNode p = tb.getTreeNode(hash, key);
- if (p != null) {
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- // assert pEntry.key.equals(key);
- if (Objects.equals(pEntry.value, oldValue)) {
- pEntry.value = newValue;
- pEntry.recordAccess(this);
- return true;
- }
- }
- }
- return false;
- }
-
- @Override
- public V replace(K key, V value) {
- if (size == 0) {
- return null;
- }
- if (key == null) {
- if (nullKeyEntry != null) {
- return putForNullKey(value);
- }
- return null;
- }
- int hash = hash(key);
- int i = indexFor(hash, table.length);
- if (table[i] instanceof Entry) {
- @SuppressWarnings("unchecked")
- Entry<K,V> e = (Entry<K,V>)table[i];
- for (; e != null; e = (Entry<K,V>)e.next) {
- if (e.hash == hash && Objects.equals(e.key, key)) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
-
- return null;
- } else if (table[i] != null) {
- TreeBin tb = ((TreeBin) table[i]);
- TreeNode p = tb.getTreeNode(hash, key);
- if (p != null) {
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- // assert pEntry.key.equals(key);
- V oldValue = pEntry.value;
- pEntry.value = value;
- pEntry.recordAccess(this);
- return oldValue;
+ if (node != null && (!matchValue || (v = node.value) == value ||
+ (value != null && value.equals(v)))) {
+ if (node instanceof TreeNode)
+ ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
+ else if (node == p)
+ tab[index] = node.next;
+ else
+ p.next = node.next;
+ ++modCount;
+ --size;
+ afterNodeRemoval(node);
+ return node;
}
}
return null;
}
- @Override
- public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
- if (table == EMPTY_TABLE) {
- inflateTable(threshold);
- }
- if (key == null) {
- if (nullKeyEntry == null || nullKeyEntry.value == null) {
- V newValue = mappingFunction.apply(key);
- if (newValue != null) {
- putForNullKey(newValue);
- }
- return newValue;
- }
- return nullKeyEntry.value;
- }
- int hash = hash(key);
- int i = indexFor(hash, table.length);
- boolean checkIfNeedTree = false; // Might we convert bin to a TreeBin?
-
- if (table[i] instanceof Entry) {
- int listSize = 0;
- @SuppressWarnings("unchecked")
- Entry<K,V> e = (Entry<K,V>)table[i];
- for (; e != null; e = (Entry<K,V>)e.next) {
- if (e.hash == hash && Objects.equals(e.key, key)) {
- V oldValue = e.value;
- if (oldValue == null) {
- V newValue = mappingFunction.apply(key);
- if (newValue != null) {
- e.value = newValue;
- e.recordAccess(this);
- }
- return newValue;
- }
- return oldValue;
- }
- listSize++;
- }
- // Didn't find, fall through to call the mapping function
- checkIfNeedTree = listSize >= TreeBin.TREE_THRESHOLD;
- } else if (table[i] != null) {
- TreeBin e = (TreeBin)table[i];
- V value = mappingFunction.apply(key);
- if (value == null) { // Return the existing value, if any
- TreeNode p = e.getTreeNode(hash, key);
- if (p != null) {
- return (V) p.entry.value;
- }
- return null;
- } else { // Put the new value into the Tree, if absent
- TreeNode p = e.putTreeNode(hash, key, value, null);
- if (p == null) { // not found, new node was added
- modCount++;
- size++;
- if (size >= threshold) {
- resize(2 * table.length);
- }
- return value;
- } else { // putTreeNode() found an existing node
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- V oldVal = pEntry.value;
- if (oldVal == null) { // only replace if maps to null
- pEntry.value = value;
- pEntry.recordAccess(this);
- return value;
- }
- return oldVal;
- }
- }
- }
- V newValue = mappingFunction.apply(key);
- if (newValue != null) { // add Entry and check for TreeBin conversion
- modCount++;
- addEntry(hash, key, newValue, i, checkIfNeedTree);
- }
-
- return newValue;
- }
-
- @Override
- public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
- if (size == 0) {
- return null;
- }
- if (key == null) {
- V oldValue;
- if (nullKeyEntry != null && (oldValue = nullKeyEntry.value) != null) {
- V newValue = remappingFunction.apply(key, oldValue);
- if (newValue != null ) {
- putForNullKey(newValue);
- return newValue;
- } else {
- removeNullKey();
- }
- }
- return null;
- }
- int hash = hash(key);
- int i = indexFor(hash, table.length);
- if (table[i] instanceof Entry) {
- @SuppressWarnings("unchecked")
- Entry<K,V> prev = (Entry<K,V>)table[i];
- Entry<K,V> e = prev;
- while (e != null) {
- Entry<K,V> next = (Entry<K,V>)e.next;
- if (e.hash == hash && Objects.equals(e.key, key)) {
- V oldValue = e.value;
- if (oldValue == null)
- break;
- V newValue = remappingFunction.apply(key, oldValue);
- if (newValue == null) {
- modCount++;
- size--;
- if (prev == e)
- table[i] = next;
- else
- prev.next = next;
- e.recordRemoval(this);
- } else {
- e.value = newValue;
- e.recordAccess(this);
- }
- return newValue;
- }
- prev = e;
- e = next;
- }
- } else if (table[i] != null) {
- TreeBin tb = (TreeBin)table[i];
- TreeNode p = tb.getTreeNode(hash, key);
- if (p != null) {
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- // assert pEntry.key.equals(key);
- V oldValue = pEntry.value;
- if (oldValue != null) {
- V newValue = remappingFunction.apply(key, oldValue);
- if (newValue == null) { // remove mapping
- modCount++;
- size--;
- tb.deleteTreeNode(p);
- pEntry.recordRemoval(this);
- if (tb.root == null || tb.first == null) {
- // assert tb.root == null && tb.first == null :
- // "TreeBin.first and root should both be null";
- // TreeBin is now empty, we should blank this bin
- table[i] = null;
- }
- } else {
- pEntry.value = newValue;
- pEntry.recordAccess(this);
- }
- return newValue;
- }
- }
- }
- return null;
- }
-
- @Override
- public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
- if (table == EMPTY_TABLE) {
- inflateTable(threshold);
- }
- if (key == null) {
- V oldValue = nullKeyEntry == null ? null : nullKeyEntry.value;
- V newValue = remappingFunction.apply(key, oldValue);
- if (newValue != oldValue || (oldValue == null && nullKeyEntry != null)) {
- if (newValue == null) {
- removeNullKey();
- } else {
- putForNullKey(newValue);
- }
- }
- return newValue;
- }
- int hash = hash(key);
- int i = indexFor(hash, table.length);
- boolean checkIfNeedTree = false; // Might we convert bin to a TreeBin?
-
- if (table[i] instanceof Entry) {
- int listSize = 0;
- @SuppressWarnings("unchecked")
- Entry<K,V> prev = (Entry<K,V>)table[i];
- Entry<K,V> e = prev;
-
- while (e != null) {
- Entry<K,V> next = (Entry<K,V>)e.next;
- if (e.hash == hash && Objects.equals(e.key, key)) {
- V oldValue = e.value;
- V newValue = remappingFunction.apply(key, oldValue);
- if (newValue != oldValue || oldValue == null) {
- if (newValue == null) {
- modCount++;
- size--;
- if (prev == e)
- table[i] = next;
- else
- prev.next = next;
- e.recordRemoval(this);
- } else {
- e.value = newValue;
- e.recordAccess(this);
- }
- }
- return newValue;
- }
- prev = e;
- e = next;
- listSize++;
- }
- checkIfNeedTree = listSize >= TreeBin.TREE_THRESHOLD;
- } else if (table[i] != null) {
- TreeBin tb = (TreeBin)table[i];
- TreeNode p = tb.getTreeNode(hash, key);
- V oldValue = p == null ? null : (V)p.entry.value;
- V newValue = remappingFunction.apply(key, oldValue);
- if (newValue != oldValue || (oldValue == null && p != null)) {
- if (newValue == null) {
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- modCount++;
- size--;
- tb.deleteTreeNode(p);
- pEntry.recordRemoval(this);
- if (tb.root == null || tb.first == null) {
- // assert tb.root == null && tb.first == null :
- // "TreeBin.first and root should both be null";
- // TreeBin is now empty, we should blank this bin
- table[i] = null;
- }
- } else {
- if (p != null) { // just update the value
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- pEntry.value = newValue;
- pEntry.recordAccess(this);
- } else { // need to put new node
- p = tb.putTreeNode(hash, key, newValue, null);
- // assert p == null; // should have added a new node
- modCount++;
- size++;
- if (size >= threshold) {
- resize(2 * table.length);
- }
- }
- }
- }
- return newValue;
- }
-
- V newValue = remappingFunction.apply(key, null);
- if (newValue != null) {
- modCount++;
- addEntry(hash, key, newValue, i, checkIfNeedTree);
- }
-
- return newValue;
- }
-
- @Override
- public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
- if (table == EMPTY_TABLE) {
- inflateTable(threshold);
- }
- if (key == null) {
- V oldValue = nullKeyEntry == null ? null : nullKeyEntry.value;
- V newValue = oldValue == null ? value : remappingFunction.apply(oldValue, value);
- if (newValue != null) {
- putForNullKey(newValue);
- } else if (nullKeyEntry != null) {
- removeNullKey();
- }
- return newValue;
- }
- int hash = hash(key);
- int i = indexFor(hash, table.length);
- boolean checkIfNeedTree = false; // Might we convert bin to a TreeBin?
-
- if (table[i] instanceof Entry) {
- int listSize = 0;
- @SuppressWarnings("unchecked")
- Entry<K,V> prev = (Entry<K,V>)table[i];
- Entry<K,V> e = prev;
-
- while (e != null) {
- Entry<K,V> next = (Entry<K,V>)e.next;
- if (e.hash == hash && Objects.equals(e.key, key)) {
- V oldValue = e.value;
- V newValue = (oldValue == null) ? value :
- remappingFunction.apply(oldValue, value);
- if (newValue == null) {
- modCount++;
- size--;
- if (prev == e)
- table[i] = next;
- else
- prev.next = next;
- e.recordRemoval(this);
- } else {
- e.value = newValue;
- e.recordAccess(this);
- }
- return newValue;
- }
- prev = e;
- e = next;
- listSize++;
- }
- // Didn't find, so fall through and (maybe) call addEntry() to add
- // the Entry and check for TreeBin conversion.
- checkIfNeedTree = listSize >= TreeBin.TREE_THRESHOLD;
- } else if (table[i] != null) {
- TreeBin tb = (TreeBin)table[i];
- TreeNode p = tb.getTreeNode(hash, key);
- V oldValue = p == null ? null : (V)p.entry.value;
- V newValue = (oldValue == null) ? value :
- remappingFunction.apply(oldValue, value);
- if (newValue == null) {
- if (p != null) {
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- modCount++;
- size--;
- tb.deleteTreeNode(p);
- pEntry.recordRemoval(this);
-
- if (tb.root == null || tb.first == null) {
- // assert tb.root == null && tb.first == null :
- // "TreeBin.first and root should both be null";
- // TreeBin is now empty, we should blank this bin
- table[i] = null;
- }
- }
- return null;
- } else if (newValue != oldValue) {
- if (p != null) { // just update the value
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- pEntry.value = newValue;
- pEntry.recordAccess(this);
- } else { // need to put new node
- p = tb.putTreeNode(hash, key, newValue, null);
- // assert p == null; // should have added a new node
- modCount++;
- size++;
- if (size >= threshold) {
- resize(2 * table.length);
- }
- }
- }
- return newValue;
- }
- if (value != null) {
- modCount++;
- addEntry(hash, key, value, i, checkIfNeedTree);
- }
- return value;
- }
-
- // end of optimized implementations of default methods in Map
-
- /**
- * Removes and returns the entry associated with the specified key
- * in the HashMap. Returns null if the HashMap contains no mapping
- * for this key.
- *
- * We don't bother converting TreeBins back to Entry lists if the bin falls
- * back below TREE_THRESHOLD, but we do clear bins when removing the last
- * TreeNode in a TreeBin.
- */
- final Entry<K,V> removeEntryForKey(Object key) {
- if (size == 0) {
- return null;
- }
- if (key == null) {
- if (nullKeyEntry != null) {
- return removeNullKey();
- }
- return null;
- }
- int hash = hash(key);
- int i = indexFor(hash, table.length);
-
- if (table[i] instanceof Entry) {
- @SuppressWarnings("unchecked")
- Entry<K,V> prev = (Entry<K,V>)table[i];
- Entry<K,V> e = prev;
-
- while (e != null) {
- @SuppressWarnings("unchecked")
- Entry<K,V> next = (Entry<K,V>) e.next;
- if (e.hash == hash && Objects.equals(e.key, key)) {
- modCount++;
- size--;
- if (prev == e)
- table[i] = next;
- else
- prev.next = next;
- e.recordRemoval(this);
- return e;
- }
- prev = e;
- e = next;
- }
- } else if (table[i] != null) {
- TreeBin tb = ((TreeBin) table[i]);
- TreeNode p = tb.getTreeNode(hash, (K)key);
- if (p != null) {
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- // assert pEntry.key.equals(key);
- modCount++;
- size--;
- tb.deleteTreeNode(p);
- pEntry.recordRemoval(this);
- if (tb.root == null || tb.first == null) {
- // assert tb.root == null && tb.first == null :
- // "TreeBin.first and root should both be null";
- // TreeBin is now empty, we should blank this bin
- table[i] = null;
- }
- return pEntry;
- }
- }
- return null;
- }
-
- /**
- * Special version of remove for EntrySet using {@code Map.Entry.equals()}
- * for matching.
- */
- final Entry<K,V> removeMapping(Object o) {
- if (size == 0 || !(o instanceof Map.Entry))
- return null;
-
- Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
- Object key = entry.getKey();
-
- if (key == null) {
- if (entry.equals(nullKeyEntry)) {
- return removeNullKey();
- }
- return null;
- }
-
- int hash = hash(key);
- int i = indexFor(hash, table.length);
-
- if (table[i] instanceof Entry) {
- @SuppressWarnings("unchecked")
- Entry<K,V> prev = (Entry<K,V>)table[i];
- Entry<K,V> e = prev;
-
- while (e != null) {
- @SuppressWarnings("unchecked")
- Entry<K,V> next = (Entry<K,V>)e.next;
- if (e.hash == hash && e.equals(entry)) {
- modCount++;
- size--;
- if (prev == e)
- table[i] = next;
- else
- prev.next = next;
- e.recordRemoval(this);
- return e;
- }
- prev = e;
- e = next;
- }
- } else if (table[i] != null) {
- TreeBin tb = ((TreeBin) table[i]);
- TreeNode p = tb.getTreeNode(hash, (K)key);
- if (p != null && p.entry.equals(entry)) {
- @SuppressWarnings("unchecked")
- Entry<K,V> pEntry = (Entry<K,V>)p.entry;
- // assert pEntry.key.equals(key);
- modCount++;
- size--;
- tb.deleteTreeNode(p);
- pEntry.recordRemoval(this);
- if (tb.root == null || tb.first == null) {
- // assert tb.root == null && tb.first == null :
- // "TreeBin.first and root should both be null";
- // TreeBin is now empty, we should blank this bin
- table[i] = null;
- }
- return pEntry;
- }
- }
- return null;
- }
-
- /*
- * Remove the mapping for the null key, and update internal accounting
- * (size, modcount, recordRemoval, etc).
- *
- * Assumes nullKeyEntry is non-null.
- */
- private Entry<K,V> removeNullKey() {
- // assert nullKeyEntry != null;
- Entry<K,V> retVal = nullKeyEntry;
- modCount++;
- size--;
- retVal.recordRemoval(this);
- nullKeyEntry = null;
- return retVal;
- }
-
/**
* Removes all of the mappings from this map.
* The map will be empty after this call returns.
*/
public void clear() {
+ Node<K,V>[] tab;
modCount++;
- if (nullKeyEntry != null) {
- nullKeyEntry = null;
+ if ((tab = table) != null && size > 0) {
+ size = 0;
+ for (int i = 0; i < tab.length; ++i)
+ tab[i] = null;
}
- Arrays.fill(table, null);
- size = 0;
}
/**
@@ -2134,352 +873,20 @@
* specified value
*/
public boolean containsValue(Object value) {
- if (value == null) {
- return containsNullValue();
- }
- Object[] tab = table;
- for (int i = 0; i < tab.length; i++) {
- if (tab[i] instanceof Entry) {
- Entry<?,?> e = (Entry<?,?>)tab[i];
- for (; e != null; e = (Entry<?,?>)e.next) {
- if (value.equals(e.value)) {
- return true;
- }
- }
- } else if (tab[i] != null) {
- TreeBin e = (TreeBin)tab[i];
- TreeNode p = e.first;
- for (; p != null; p = (TreeNode) p.entry.next) {
- if (value == p.entry.value || value.equals(p.entry.value)) {
+ Node<K,V>[] tab; V v;
+ if ((tab = table) != null && size > 0) {
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next) {
+ if ((v = e.value) == value ||
+ (value != null && value.equals(v)))
return true;
- }
- }
- }
- }
- // Didn't find value in table - could be in nullKeyEntry
- return (nullKeyEntry != null && (value == nullKeyEntry.value ||
- value.equals(nullKeyEntry.value)));
- }
-
- /**
- * Special-case code for containsValue with null argument
- */
- private boolean containsNullValue() {
- Object[] tab = table;
- for (int i = 0; i < tab.length; i++) {
- if (tab[i] instanceof Entry) {
- Entry<K,V> e = (Entry<K,V>)tab[i];
- for (; e != null; e = (Entry<K,V>)e.next) {
- if (e.value == null) {
- return true;
- }
- }
- } else if (tab[i] != null) {
- TreeBin e = (TreeBin)tab[i];
- TreeNode p = e.first;
- for (; p != null; p = (TreeNode) p.entry.next) {
- if (p.entry.value == null) {
- return true;
- }
}
}
}
- // Didn't find value in table - could be in nullKeyEntry
- return (nullKeyEntry != null && nullKeyEntry.value == null);
- }
-
- /**
- * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
- * values themselves are not cloned.
- *
- * @return a shallow copy of this map
- */
- @SuppressWarnings("unchecked")
- public Object clone() {
- HashMap<K,V> result = null;
- try {
- result = (HashMap<K,V>)super.clone();
- } catch (CloneNotSupportedException e) {
- // assert false;
- }
- if (result.table != EMPTY_TABLE) {
- result.inflateTable(Math.min(
- (int) Math.min(
- size * Math.min(1 / loadFactor, 4.0f),
- // we have limits...
- HashMap.MAXIMUM_CAPACITY),
- table.length));
- }
- result.entrySet = null;
- result.modCount = 0;
- result.size = 0;
- result.nullKeyEntry = null;
- result.init();
- result.putAllForCreate(this);
-
- return result;
- }
-
- static class Entry<K,V> implements Map.Entry<K,V> {
- final K key;
- V value;
- Object next; // an Entry, or a TreeNode
- final int hash;
-
- /**
- * Creates new entry.
- */
- Entry(int h, K k, V v, Object n) {
- value = v;
- next = n;
- key = k;
- hash = h;
- }
-
- public final K getKey() {
- return key;
- }
-
- public final V getValue() {
- return value;
- }
-
- public final V setValue(V newValue) {
- V oldValue = value;
- value = newValue;
- return oldValue;
- }
-
- public final boolean equals(Object o) {
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry<?,?> e = (Map.Entry<?,?>)o;
- Object k1 = getKey();
- Object k2 = e.getKey();
- if (k1 == k2 || (k1 != null && k1.equals(k2))) {
- Object v1 = getValue();
- Object v2 = e.getValue();
- if (v1 == v2 || (v1 != null && v1.equals(v2)))
- return true;
- }
- return false;
- }
-
- public final int hashCode() {
- return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
- }
-
- public final String toString() {
- return getKey() + "=" + getValue();
- }
-
- /**
- * This method is invoked whenever the value in an entry is
- * overwritten for a key that's already in the HashMap.
- */
- void recordAccess(HashMap<K,V> m) {
- }
-
- /**
- * This method is invoked whenever the entry is
- * removed from the table.
- */
- void recordRemoval(HashMap<K,V> m) {
- }
- }
-
- void addEntry(int hash, K key, V value, int bucketIndex) {
- addEntry(hash, key, value, bucketIndex, true);
- }
-
- /**
- * Adds a new entry with the specified key, value and hash code to
- * the specified bucket. It is the responsibility of this
- * method to resize the table if appropriate. The new entry is then
- * created by calling createEntry().
- *
- * Subclass overrides this to alter the behavior of put method.
- *
- * If checkIfNeedTree is false, it is known that this bucket will not need
- * to be converted to a TreeBin, so don't bothering checking.
- *
- * Assumes key is not null.
- */
- void addEntry(int hash, K key, V value, int bucketIndex, boolean checkIfNeedTree) {
- // assert key != null;
- if ((size >= threshold) && (null != table[bucketIndex])) {
- resize(2 * table.length);
- hash = hash(key);
- bucketIndex = indexFor(hash, table.length);
- }
- createEntry(hash, key, value, bucketIndex, checkIfNeedTree);
+ return false;
}
/**
- * Called by addEntry(), and also used when creating entries
- * as part of Map construction or "pseudo-construction" (cloning,
- * deserialization). This version does not check for resizing of the table.
- *
- * This method is responsible for converting a bucket to a TreeBin once
- * TREE_THRESHOLD is reached. However if checkIfNeedTree is false, it is known
- * that this bucket will not need to be converted to a TreeBin, so don't
- * bother checking. The new entry is constructed by calling newEntry().
- *
- * Assumes key is not null.
- *
- * Note: buckets already converted to a TreeBin don't call this method, but
- * instead call TreeBin.putTreeNode() to create new entries.
- */
- void createEntry(int hash, K key, V value, int bucketIndex, boolean checkIfNeedTree) {
- // assert key != null;
- @SuppressWarnings("unchecked")
- Entry<K,V> e = (Entry<K,V>)table[bucketIndex];
- table[bucketIndex] = newEntry(hash, key, value, e);
- size++;
-
- if (checkIfNeedTree) {
- int listSize = 0;
- for (e = (Entry<K,V>) table[bucketIndex]; e != null; e = (Entry<K,V>)e.next) {
- listSize++;
- if (listSize >= TreeBin.TREE_THRESHOLD) { // Convert to TreeBin
- if (comparableClassFor(key) != null) {
- TreeBin t = new TreeBin();
- t.populate((Entry)table[bucketIndex]);
- table[bucketIndex] = t;
- }
- break;
- }
- }
- }
- }
-
- /*
- * Factory method to create a new Entry object.
- */
- Entry<K,V> newEntry(int hash, K key, V value, Object next) {
- return new HashMap.Entry<>(hash, key, value, next);
- }
-
-
- private abstract class HashIterator<E> implements Iterator<E> {
- Object next; // next entry to return, an Entry or a TreeNode
- int expectedModCount; // For fast-fail
- int index; // current slot
- Object current; // current entry, an Entry or a TreeNode
-
- HashIterator() {
- expectedModCount = modCount;
- if (size > 0) { // advance to first entry
- if (nullKeyEntry != null) {
- // assert nullKeyEntry.next == null;
- // This works with nextEntry(): nullKeyEntry isa Entry, and
- // e.next will be null, so we'll hit the findNextBin() call.
- next = nullKeyEntry;
- } else {
- findNextBin();
- }
- }
- }
-
- public final boolean hasNext() {
- return next != null;
- }
-
- @SuppressWarnings("unchecked")
- final Entry<K,V> nextEntry() {
- if (modCount != expectedModCount) {
- throw new ConcurrentModificationException();
- }
- Object e = next;
- Entry<K,V> retVal;
-
- if (e == null)
- throw new NoSuchElementException();
-
- if (e instanceof TreeNode) { // TreeBin
- retVal = (Entry<K,V>)((TreeNode)e).entry;
- next = retVal.next;
- } else {
- retVal = (Entry<K,V>)e;
- next = ((Entry<K,V>)e).next;
- }
-
- if (next == null) { // Move to next bin
- findNextBin();
- }
- current = e;
- return retVal;
- }
-
- public void remove() {
- if (current == null)
- throw new IllegalStateException();
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- K k;
-
- if (current instanceof Entry) {
- k = ((Entry<K,V>)current).key;
- } else {
- k = ((Entry<K,V>)((TreeNode)current).entry).key;
-
- }
- current = null;
- HashMap.this.removeEntryForKey(k);
- expectedModCount = modCount;
- }
-
- /*
- * Set 'next' to the first entry of the next non-empty bin in the table
- */
- private void findNextBin() {
- // assert next == null;
- Object[] t = table;
-
- while (index < t.length && (next = t[index++]) == null)
- ;
- if (next instanceof HashMap.TreeBin) { // Point to the first TreeNode
- next = ((TreeBin) next).first;
- // assert next != null; // There should be no empty TreeBins
- }
- }
- }
-
- private final class ValueIterator extends HashIterator<V> {
- public V next() {
- return nextEntry().value;
- }
- }
-
- private final class KeyIterator extends HashIterator<K> {
- public K next() {
- return nextEntry().getKey();
- }
- }
-
- private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
- public Map.Entry<K,V> next() {
- return nextEntry();
- }
- }
-
- // Subclass overrides these to alter behavior of views' iterator() method
- Iterator<K> newKeyIterator() {
- return new KeyIterator();
- }
- Iterator<V> newValueIterator() {
- return new ValueIterator();
- }
- Iterator<Map.Entry<K,V>> newEntryIterator() {
- return new EntryIterator();
- }
-
-
- // Views
-
- private transient Set<Map.Entry<K,V>> entrySet = null;
-
- /**
* Returns a {@link Set} view of the keys contained in this map.
* The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. If the map is modified
@@ -2491,35 +898,38 @@
* <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
* operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
* operations.
+ *
+ * @return a set view of the keys contained in this map
*/
public Set<K> keySet() {
- Set<K> ks = keySet;
- return (ks != null ? ks : (keySet = new KeySet()));
+ Set<K> ks;
+ return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
}
- private final class KeySet extends AbstractSet<K> {
- public Iterator<K> iterator() {
- return newKeyIterator();
+ final class KeySet extends AbstractSet<K> {
+ public final int size() { return size; }
+ public final void clear() { HashMap.this.clear(); }
+ public final Iterator<K> iterator() { return new KeyIterator(); }
+ public final boolean contains(Object o) { return containsKey(o); }
+ public final boolean remove(Object key) {
+ return removeNode(hash(key), key, null, false, true) != null;
}
- public int size() {
- return size;
- }
- public boolean contains(Object o) {
- return containsKey(o);
+ public final Spliterator<K> spliterator() {
+ return new KeySpliterator<K,V>(HashMap.this, 0, -1, 0, 0);
}
- public boolean remove(Object o) {
- return HashMap.this.removeEntryForKey(o) != null;
- }
- public void clear() {
- HashMap.this.clear();
- }
-
- public Spliterator<K> spliterator() {
- if (HashMap.this.getClass() == HashMap.class)
- return new KeySpliterator<K,V>(HashMap.this, 0, -1, 0, 0);
- else
- return Spliterators.spliterator
- (this, Spliterator.SIZED | Spliterator.DISTINCT);
+ public final void forEach(Consumer<? super K> action) {
+ Node<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next)
+ action.accept(e.key);
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
}
}
@@ -2535,32 +945,35 @@
* <tt>Collection.remove</tt>, <tt>removeAll</tt>,
* <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
* support the <tt>add</tt> or <tt>addAll</tt> operations.
+ *
+ * @return a view of the values contained in this map
*/
public Collection<V> values() {
- Collection<V> vs = values;
- return (vs != null ? vs : (values = new Values()));
+ Collection<V> vs;
+ return (vs = values) == null ? (values = new Values()) : vs;
}
- private final class Values extends AbstractCollection<V> {
- public Iterator<V> iterator() {
- return newValueIterator();
- }
- public int size() {
- return size;
- }
- public boolean contains(Object o) {
- return containsValue(o);
+ final class Values extends AbstractCollection<V> {
+ public final int size() { return size; }
+ public final void clear() { HashMap.this.clear(); }
+ public final Iterator<V> iterator() { return new ValueIterator(); }
+ public final boolean contains(Object o) { return containsValue(o); }
+ public final Spliterator<V> spliterator() {
+ return new ValueSpliterator<K,V>(HashMap.this, 0, -1, 0, 0);
}
- public void clear() {
- HashMap.this.clear();
- }
-
- public Spliterator<V> spliterator() {
- if (HashMap.this.getClass() == HashMap.class)
- return new ValueSpliterator<K,V>(HashMap.this, 0, -1, 0, 0);
- else
- return Spliterators.spliterator
- (this, Spliterator.SIZED);
+ public final void forEach(Consumer<? super V> action) {
+ Node<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next)
+ action.accept(e.value);
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
}
}
@@ -2581,42 +994,324 @@
* @return a set view of the mappings contained in this map
*/
public Set<Map.Entry<K,V>> entrySet() {
- return entrySet0();
+ Set<Map.Entry<K,V>> es;
+ return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
- private Set<Map.Entry<K,V>> entrySet0() {
- Set<Map.Entry<K,V>> es = entrySet;
- return es != null ? es : (entrySet = new EntrySet());
- }
-
- private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
- public Iterator<Map.Entry<K,V>> iterator() {
- return newEntryIterator();
+ final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
+ public final int size() { return size; }
+ public final void clear() { HashMap.this.clear(); }
+ public final Iterator<Map.Entry<K,V>> iterator() {
+ return new EntryIterator();
}
- public boolean contains(Object o) {
+ public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
- Entry<K,V> candidate = getEntry(e.getKey());
+ Object key = e.getKey();
+ Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
- public boolean remove(Object o) {
- return removeMapping(o) != null;
+ public final boolean remove(Object o) {
+ if (o instanceof Map.Entry) {
+ Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+ Object key = e.getKey();
+ Object value = e.getValue();
+ return removeNode(hash(key), key, value, true, true) != null;
+ }
+ return false;
+ }
+ public final Spliterator<Map.Entry<K,V>> spliterator() {
+ return new EntrySpliterator<K,V>(HashMap.this, 0, -1, 0, 0);
+ }
+ public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
+ Node<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next)
+ action.accept(e);
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+
+ // Overrides of JDK8 Map extension methods
+
+ public V getOrDefault(Object key, V defaultValue) {
+ Node<K,V> e;
+ return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
+ }
+
+ public V putIfAbsent(K key, V value) {
+ return putVal(hash(key), key, value, true, true);
+ }
+
+ public boolean remove(Object key, Object value) {
+ return removeNode(hash(key), key, value, true, true) != null;
+ }
+
+ public boolean replace(K key, V oldValue, V newValue) {
+ Node<K,V> e; V v;
+ if ((e = getNode(hash(key), key)) != null &&
+ ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
+ e.value = newValue;
+ afterNodeAccess(e);
+ return true;
+ }
+ return false;
+ }
+
+ public V replace(K key, V value) {
+ Node<K,V> e;
+ if ((e = getNode(hash(key), key)) != null) {
+ V oldValue = e.value;
+ e.value = value;
+ afterNodeAccess(e);
+ return oldValue;
}
- public int size() {
- return size;
+ return null;
+ }
+
+ public V computeIfAbsent(K key,
+ Function<? super K, ? extends V> mappingFunction) {
+ if (mappingFunction == null)
+ throw new NullPointerException();
+ int hash = hash(key);
+ Node<K,V>[] tab; Node<K,V> first; int n, i;
+ int binCount = 0;
+ TreeNode<K,V> t = null;
+ Node<K,V> old = null;
+ if (size > threshold || (tab = table) == null ||
+ (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((first = tab[i = (n - 1) & hash]) != null) {
+ if (first instanceof TreeNode)
+ old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
+ else {
+ Node<K,V> e = first; K k;
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k)))) {
+ old = e;
+ break;
+ }
+ ++binCount;
+ } while ((e = e.next) != null);
+ }
+ V oldValue;
+ if (old != null && (oldValue = old.value) != null) {
+ afterNodeAccess(old);
+ return oldValue;
+ }
}
- public void clear() {
- HashMap.this.clear();
+ V v = mappingFunction.apply(key);
+ if (old != null) {
+ old.value = v;
+ afterNodeAccess(old);
+ return v;
+ }
+ else if (v == null)
+ return null;
+ else if (t != null)
+ t.putTreeVal(this, tab, hash, key, v);
+ else {
+ tab[i] = newNode(hash, key, v, first);
+ if (binCount >= TREEIFY_THRESHOLD - 1)
+ treeifyBin(tab, hash);
+ }
+ ++modCount;
+ ++size;
+ afterNodeInsertion(true);
+ return v;
+ }
+
+ public V computeIfPresent(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ Node<K,V> e; V oldValue;
+ int hash = hash(key);
+ if ((e = getNode(hash, key)) != null &&
+ (oldValue = e.value) != null) {
+ V v = remappingFunction.apply(key, oldValue);
+ if (v != null) {
+ e.value = v;
+ afterNodeAccess(e);
+ return v;
+ }
+ else
+ removeNode(hash, key, null, false, true);
}
+ return null;
+ }
- public Spliterator<Map.Entry<K,V>> spliterator() {
- if (HashMap.this.getClass() == HashMap.class)
- return new EntrySpliterator<K,V>(HashMap.this, 0, -1, 0, 0);
+ public V compute(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ if (remappingFunction == null)
+ throw new NullPointerException();
+ int hash = hash(key);
+ Node<K,V>[] tab; Node<K,V> first; int n, i;
+ int binCount = 0;
+ TreeNode<K,V> t = null;
+ Node<K,V> old = null;
+ if (size > threshold || (tab = table) == null ||
+ (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((first = tab[i = (n - 1) & hash]) != null) {
+ if (first instanceof TreeNode)
+ old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
+ else {
+ Node<K,V> e = first; K k;
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k)))) {
+ old = e;
+ break;
+ }
+ ++binCount;
+ } while ((e = e.next) != null);
+ }
+ }
+ V oldValue = (old == null) ? null : old.value;
+ V v = remappingFunction.apply(key, oldValue);
+ if (old != null) {
+ if (v != null) {
+ old.value = v;
+ afterNodeAccess(old);
+ }
else
- return Spliterators.spliterator
- (this, Spliterator.SIZED | Spliterator.DISTINCT);
+ removeNode(hash, key, null, false, true);
+ }
+ else if (v != null) {
+ if (t != null)
+ t.putTreeVal(this, tab, hash, key, v);
+ else {
+ tab[i] = newNode(hash, key, v, first);
+ if (binCount >= TREEIFY_THRESHOLD - 1)
+ treeifyBin(tab, hash);
+ }
+ ++modCount;
+ ++size;
+ afterNodeInsertion(true);
+ }
+ return v;
+ }
+
+ public V merge(K key, V value,
+ BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ if (remappingFunction == null)
+ throw new NullPointerException();
+ int hash = hash(key);
+ Node<K,V>[] tab; Node<K,V> first; int n, i;
+ int binCount = 0;
+ TreeNode<K,V> t = null;
+ Node<K,V> old = null;
+ if (size > threshold || (tab = table) == null ||
+ (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((first = tab[i = (n - 1) & hash]) != null) {
+ if (first instanceof TreeNode)
+ old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
+ else {
+ Node<K,V> e = first; K k;
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k)))) {
+ old = e;
+ break;
+ }
+ ++binCount;
+ } while ((e = e.next) != null);
+ }
+ }
+ if (old != null) {
+ V v = remappingFunction.apply(old.value, value);
+ if (v != null) {
+ old.value = v;
+ afterNodeAccess(old);
+ }
+ else
+ removeNode(hash, key, null, false, true);
+ return v;
}
+ if (value != null) {
+ if (t != null)
+ t.putTreeVal(this, tab, hash, key, value);
+ else {
+ tab[i] = newNode(hash, key, value, first);
+ if (binCount >= TREEIFY_THRESHOLD - 1)
+ treeifyBin(tab, hash);
+ }
+ ++modCount;
+ ++size;
+ afterNodeInsertion(true);
+ }
+ return value;
+ }
+
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ Node<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next)
+ action.accept(e.key, e.value);
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+ Node<K,V>[] tab;
+ if (function == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next) {
+ e.value = function.apply(e.key, e.value);
+ }
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ // Cloning and serialization
+
+ /**
+ * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
+ * values themselves are not cloned.
+ *
+ * @return a shallow copy of this map
+ */
+ @SuppressWarnings("unchecked")
+ public Object clone() {
+ HashMap<K,V> result;
+ try {
+ result = (HashMap<K,V>)super.clone();
+ } catch (CloneNotSupportedException e) {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError(e);
+ }
+ result.reinitialize();
+ result.putMapEntries(this, false);
+ return result;
+ }
+
+ // These methods are also used when serializing HashSets
+ final float loadFactor() { return loadFactor; }
+ final int capacity() {
+ return (table != null) ? table.length :
+ (threshold > 0) ? threshold :
+ DEFAULT_INITIAL_CAPACITY;
}
/**
@@ -2631,118 +1326,143 @@
* emitted in no particular order.
*/
private void writeObject(java.io.ObjectOutputStream s)
- throws IOException
- {
+ throws IOException {
+ int buckets = capacity();
// Write out the threshold, loadfactor, and any hidden stuff
s.defaultWriteObject();
-
- // Write out number of buckets
- if (table==EMPTY_TABLE) {
- s.writeInt(roundUpToPowerOf2(threshold));
- } else {
- s.writeInt(table.length);
- }
-
- // Write out size (number of Mappings)
+ s.writeInt(buckets);
s.writeInt(size);
-
- // Write out keys and values (alternating)
- if (size > 0) {
- for(Map.Entry<K,V> e : entrySet0()) {
- s.writeObject(e.getKey());
- s.writeObject(e.getValue());
- }
- }
+ internalWriteEntries(s);
}
- private static final long serialVersionUID = 362498820763181265L;
-
/**
* Reconstitute the {@code HashMap} instance from a stream (i.e.,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
- throws IOException, ClassNotFoundException
- {
+ throws IOException, ClassNotFoundException {
// Read in the threshold (ignored), loadfactor, and any hidden stuff
s.defaultReadObject();
- if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
+ reinitialize();
+ if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new InvalidObjectException("Illegal load factor: " +
- loadFactor);
- }
-
- // set other fields that need values
- if (Holder.USE_HASHSEED) {
- int seed = ThreadLocalRandom.current().nextInt();
- Holder.UNSAFE.putIntVolatile(this, Holder.HASHSEED_OFFSET,
- (seed != 0) ? seed : 1);
- }
- table = EMPTY_TABLE;
-
- // Read in number of buckets
- s.readInt(); // ignored.
-
- // Read number of mappings
- int mappings = s.readInt();
+ loadFactor);
+ s.readInt(); // Read and ignore number of buckets
+ int mappings = s.readInt(); // Read number of mappings (size)
if (mappings < 0)
throw new InvalidObjectException("Illegal mappings count: " +
- mappings);
-
- // capacity chosen by number of mappings and desired load (if >= 0.25)
- int capacity = (int) Math.min(
- mappings * Math.min(1 / loadFactor, 4.0f),
- // we have limits...
- HashMap.MAXIMUM_CAPACITY);
+ mappings);
+ else if (mappings > 0) { // (if zero, use defaults)
+ // Size the table using given load factor only if within
+ // range of 0.25...4.0
+ float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
+ float fc = (float)mappings / lf + 1.0f;
+ int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
+ DEFAULT_INITIAL_CAPACITY :
+ (fc >= MAXIMUM_CAPACITY) ?
+ MAXIMUM_CAPACITY :
+ tableSizeFor((int)fc));
+ float ft = (float)cap * lf;
+ threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
+ (int)ft : Integer.MAX_VALUE);
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
+ table = tab;
- // allocate the bucket array;
- if (mappings > 0) {
- inflateTable(capacity);
- } else {
- threshold = capacity;
- }
-
- init(); // Give subclass a chance to do its thing.
-
- // Read the keys and values, and put the mappings in the HashMap
- for (int i=0; i<mappings; i++) {
- @SuppressWarnings("unchecked")
- K key = (K) s.readObject();
- @SuppressWarnings("unchecked")
- V value = (V) s.readObject();
- putForCreate(key, value);
+ // Read the keys and values, and put the mappings in the HashMap
+ for (int i = 0; i < mappings; i++) {
+ @SuppressWarnings("unchecked")
+ K key = (K) s.readObject();
+ @SuppressWarnings("unchecked")
+ V value = (V) s.readObject();
+ putVal(hash(key), key, value, false, false);
+ }
}
}
- // These methods are used when serializing HashSets
- int capacity() { return table.length; }
- float loadFactor() { return loadFactor; }
+ /* ------------------------------------------------------------ */
+ // iterators
+
+ abstract class HashIterator {
+ Node<K,V> next; // next entry to return
+ Node<K,V> current; // current entry
+ int expectedModCount; // for fast-fail
+ int index; // current slot
+
+ HashIterator() {
+ expectedModCount = modCount;
+ Node<K,V>[] t = table;
+ current = next = null;
+ index = 0;
+ if (t != null && size > 0) { // advance to first entry
+ do {} while (index < t.length && (next = t[index++]) == null);
+ }
+ }
+
+ public final boolean hasNext() {
+ return next != null;
+ }
- /**
- * Standin until HM overhaul; based loosely on Weak and Identity HM.
- */
+ final Node<K,V> nextNode() {
+ Node<K,V>[] t;
+ Node<K,V> e = next;
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (e == null)
+ throw new NoSuchElementException();
+ if ((next = (current = e).next) == null && (t = table) != null) {
+ do {} while (index < t.length && (next = t[index++]) == null);
+ }
+ return e;
+ }
+
+ public final void remove() {
+ Node<K,V> p = current;
+ if (p == null)
+ throw new IllegalStateException();
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ current = null;
+ K key = p.key;
+ removeNode(hash(key), key, null, false, false);
+ expectedModCount = modCount;
+ }
+ }
+
+ final class KeyIterator extends HashIterator
+ implements Iterator<K> {
+ public final K next() { return nextNode().key; }
+ }
+
+ final class ValueIterator extends HashIterator
+ implements Iterator<V> {
+ public final V next() { return nextNode().value; }
+ }
+
+ final class EntryIterator extends HashIterator
+ implements Iterator<Map.Entry<K,V>> {
+ public final Map.Entry<K,V> next() { return nextNode(); }
+ }
+
+ /* ------------------------------------------------------------ */
+ // spliterators
+
static class HashMapSpliterator<K,V> {
final HashMap<K,V> map;
- Object current; // current node, can be Entry or TreeNode
+ Node<K,V> current; // current node
int index; // current index, modified on advance/split
int fence; // one past last index
int est; // size estimate
int expectedModCount; // for comodification checks
- boolean acceptedNull; // Have we accepted the null key?
- // Without this, we can't distinguish
- // between being at the very beginning (and
- // needing to accept null), or being at the
- // end of the list in bin 0. In both cases,
- // current == null && index == 0.
HashMapSpliterator(HashMap<K,V> m, int origin,
- int fence, int est,
- int expectedModCount) {
+ int fence, int est,
+ int expectedModCount) {
this.map = m;
this.index = origin;
this.fence = fence;
this.est = est;
this.expectedModCount = expectedModCount;
- this.acceptedNull = false;
}
final int getFence() { // initialize fence and size on first use
@@ -2751,7 +1471,8 @@
HashMap<K,V> m = map;
est = m.size;
expectedModCount = m.modCount;
- hi = fence = m.table.length;
+ Node<K,V>[] tab = m.table;
+ hi = fence = (tab == null) ? 0 : tab.length;
}
return hi;
}
@@ -2772,56 +1493,33 @@
public KeySpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
- if (lo >= mid || current != null) {
- return null;
- } else {
- KeySpliterator<K,V> retVal = new KeySpliterator<K,V>(map, lo,
- index = mid, est >>>= 1, expectedModCount);
- // Only 'this' Spliterator chould check for null.
- retVal.acceptedNull = true;
- return retVal;
- }
+ return (lo >= mid || current != null) ? null :
+ new KeySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
+ expectedModCount);
}
- @SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super K> action) {
int i, hi, mc;
if (action == null)
throw new NullPointerException();
HashMap<K,V> m = map;
- Object[] tab = m.table;
+ Node<K,V>[] tab = m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
- hi = fence = tab.length;
+ hi = fence = (tab == null) ? 0 : tab.length;
}
else
mc = expectedModCount;
-
- if (!acceptedNull) {
- acceptedNull = true;
- if (m.nullKeyEntry != null) {
- action.accept(m.nullKeyEntry.key);
- }
- }
- if (tab.length >= hi && (i = index) >= 0 &&
- (i < (index = hi) || current != null)) {
- Object p = current;
+ if (tab != null && tab.length >= hi &&
+ (i = index) >= 0 && (i < (index = hi) || current != null)) {
+ Node<K,V> p = current;
current = null;
do {
- if (p == null) {
+ if (p == null)
p = tab[i++];
- if (p instanceof HashMap.TreeBin) {
- p = ((HashMap.TreeBin)p).first;
- }
- } else {
- HashMap.Entry<K,V> entry;
- if (p instanceof HashMap.Entry) {
- entry = (HashMap.Entry<K,V>)p;
- } else {
- entry = (HashMap.Entry<K,V>)((TreeNode)p).entry;
- }
- action.accept(entry.key);
- p = entry.next;
+ else {
+ action.accept(p.key);
+ p = p.next;
}
} while (p != null || i < hi);
if (m.modCount != mc)
@@ -2829,39 +1527,18 @@
}
}
- @SuppressWarnings("unchecked")
public boolean tryAdvance(Consumer<? super K> action) {
int hi;
if (action == null)
throw new NullPointerException();
- Object[] tab = map.table;
- hi = getFence();
-
- if (!acceptedNull) {
- acceptedNull = true;
- if (map.nullKeyEntry != null) {
- action.accept(map.nullKeyEntry.key);
- if (map.modCount != expectedModCount)
- throw new ConcurrentModificationException();
- return true;
- }
- }
- if (tab.length >= hi && index >= 0) {
+ Node<K,V>[] tab = map.table;
+ if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
- if (current == null) {
+ if (current == null)
current = tab[index++];
- if (current instanceof HashMap.TreeBin) {
- current = ((HashMap.TreeBin)current).first;
- }
- } else {
- HashMap.Entry<K,V> entry;
- if (current instanceof HashMap.Entry) {
- entry = (HashMap.Entry<K,V>)current;
- } else {
- entry = (HashMap.Entry<K,V>)((TreeNode)current).entry;
- }
- K k = entry.key;
- current = entry.next;
+ else {
+ K k = current.key;
+ current = current.next;
action.accept(k);
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
@@ -2888,56 +1565,33 @@
public ValueSpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
- if (lo >= mid || current != null) {
- return null;
- } else {
- ValueSpliterator<K,V> retVal = new ValueSpliterator<K,V>(map,
- lo, index = mid, est >>>= 1, expectedModCount);
- // Only 'this' Spliterator chould check for null.
- retVal.acceptedNull = true;
- return retVal;
- }
+ return (lo >= mid || current != null) ? null :
+ new ValueSpliterator<K,V>(map, lo, index = mid, est >>>= 1,
+ expectedModCount);
}
- @SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super V> action) {
int i, hi, mc;
if (action == null)
throw new NullPointerException();
HashMap<K,V> m = map;
- Object[] tab = m.table;
+ Node<K,V>[] tab = m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
- hi = fence = tab.length;
+ hi = fence = (tab == null) ? 0 : tab.length;
}
else
mc = expectedModCount;
-
- if (!acceptedNull) {
- acceptedNull = true;
- if (m.nullKeyEntry != null) {
- action.accept(m.nullKeyEntry.value);
- }
- }
- if (tab.length >= hi && (i = index) >= 0 &&
- (i < (index = hi) || current != null)) {
- Object p = current;
+ if (tab != null && tab.length >= hi &&
+ (i = index) >= 0 && (i < (index = hi) || current != null)) {
+ Node<K,V> p = current;
current = null;
do {
- if (p == null) {
+ if (p == null)
p = tab[i++];
- if (p instanceof HashMap.TreeBin) {
- p = ((HashMap.TreeBin)p).first;
- }
- } else {
- HashMap.Entry<K,V> entry;
- if (p instanceof HashMap.Entry) {
- entry = (HashMap.Entry<K,V>)p;
- } else {
- entry = (HashMap.Entry<K,V>)((TreeNode)p).entry;
- }
- action.accept(entry.value);
- p = entry.next;
+ else {
+ action.accept(p.value);
+ p = p.next;
}
} while (p != null || i < hi);
if (m.modCount != mc)
@@ -2945,39 +1599,18 @@
}
}
- @SuppressWarnings("unchecked")
public boolean tryAdvance(Consumer<? super V> action) {
int hi;
if (action == null)
throw new NullPointerException();
- Object[] tab = map.table;
- hi = getFence();
-
- if (!acceptedNull) {
- acceptedNull = true;
- if (map.nullKeyEntry != null) {
- action.accept(map.nullKeyEntry.value);
- if (map.modCount != expectedModCount)
- throw new ConcurrentModificationException();
- return true;
- }
- }
- if (tab.length >= hi && index >= 0) {
+ Node<K,V>[] tab = map.table;
+ if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
- if (current == null) {
+ if (current == null)
current = tab[index++];
- if (current instanceof HashMap.TreeBin) {
- current = ((HashMap.TreeBin)current).first;
- }
- } else {
- HashMap.Entry<K,V> entry;
- if (current instanceof HashMap.Entry) {
- entry = (Entry<K,V>)current;
- } else {
- entry = (Entry<K,V>)((TreeNode)current).entry;
- }
- V v = entry.value;
- current = entry.next;
+ else {
+ V v = current.value;
+ current = current.next;
action.accept(v);
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
@@ -3003,57 +1636,33 @@
public EntrySpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
- if (lo >= mid || current != null) {
- return null;
- } else {
- EntrySpliterator<K,V> retVal = new EntrySpliterator<K,V>(map,
- lo, index = mid, est >>>= 1, expectedModCount);
- // Only 'this' Spliterator chould check for null.
- retVal.acceptedNull = true;
- return retVal;
- }
+ return (lo >= mid || current != null) ? null :
+ new EntrySpliterator<K,V>(map, lo, index = mid, est >>>= 1,
+ expectedModCount);
}
- @SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
int i, hi, mc;
if (action == null)
throw new NullPointerException();
HashMap<K,V> m = map;
- Object[] tab = m.table;
+ Node<K,V>[] tab = m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
- hi = fence = tab.length;
+ hi = fence = (tab == null) ? 0 : tab.length;
}
else
mc = expectedModCount;
-
- if (!acceptedNull) {
- acceptedNull = true;
- if (m.nullKeyEntry != null) {
- action.accept(m.nullKeyEntry);
- }
- }
- if (tab.length >= hi && (i = index) >= 0 &&
- (i < (index = hi) || current != null)) {
- Object p = current;
+ if (tab != null && tab.length >= hi &&
+ (i = index) >= 0 && (i < (index = hi) || current != null)) {
+ Node<K,V> p = current;
current = null;
do {
- if (p == null) {
+ if (p == null)
p = tab[i++];
- if (p instanceof HashMap.TreeBin) {
- p = ((HashMap.TreeBin)p).first;
- }
- } else {
- HashMap.Entry<K,V> entry;
- if (p instanceof HashMap.Entry) {
- entry = (HashMap.Entry<K,V>)p;
- } else {
- entry = (HashMap.Entry<K,V>)((TreeNode)p).entry;
- }
- action.accept(entry);
- p = entry.next;
-
+ else {
+ action.accept(p);
+ p = p.next;
}
} while (p != null || i < hi);
if (m.modCount != mc)
@@ -3061,38 +1670,18 @@
}
}
- @SuppressWarnings("unchecked")
public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
int hi;
if (action == null)
throw new NullPointerException();
- Object[] tab = map.table;
- hi = getFence();
-
- if (!acceptedNull) {
- acceptedNull = true;
- if (map.nullKeyEntry != null) {
- action.accept(map.nullKeyEntry);
- if (map.modCount != expectedModCount)
- throw new ConcurrentModificationException();
- return true;
- }
- }
- if (tab.length >= hi && index >= 0) {
+ Node<K,V>[] tab = map.table;
+ if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
- if (current == null) {
+ if (current == null)
current = tab[index++];
- if (current instanceof HashMap.TreeBin) {
- current = ((HashMap.TreeBin)current).first;
- }
- } else {
- HashMap.Entry<K,V> e;
- if (current instanceof HashMap.Entry) {
- e = (Entry<K,V>)current;
- } else {
- e = (Entry<K,V>)((TreeNode)current).entry;
- }
- current = e.next;
+ else {
+ Node<K,V> e = current;
+ current = current.next;
action.accept(e);
if (map.modCount != expectedModCount)
throw new ConcurrentModificationException();
@@ -3108,4 +1697,664 @@
Spliterator.DISTINCT;
}
}
+
+ /* ------------------------------------------------------------ */
+ // LinkedHashMap support
+
+
+ /*
+ * The following package-protected methods are designed to be
+ * overridden by LinkedHashMap, but not by any other subclass.
+ * Nearly all other internal methods are also package-protected
+ * but are declared final, so can be used by LinkedHashMap, view
+ * classes, and HashSet.
+ */
+
+ // Create a regular (non-tree) node
+ Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
+ return new Node<K,V>(hash, key, value, next);
+ }
+
+ // For conversion from TreeNodes to plain nodes
+ Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
+ return new Node<K,V>(p.hash, p.key, p.value, next);
+ }
+
+ // Create a tree bin node
+ TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
+ return new TreeNode<K,V>(hash, key, value, next);
+ }
+
+ // For treeifyBin
+ TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
+ return new TreeNode<K,V>(p.hash, p.key, p.value, next);
+ }
+
+ /**
+ * Reset to initial default state. Called by clone and readObject.
+ */
+ void reinitialize() {
+ table = null;
+ entrySet = null;
+ keySet = null;
+ values = null;
+ modCount = 0;
+ threshold = 0;
+ size = 0;
+ }
+
+ // Callbacks to allow LinkedHashMap post-actions
+ void afterNodeAccess(Node<K,V> p) { }
+ void afterNodeInsertion(boolean evict) { }
+ void afterNodeRemoval(Node<K,V> p) { }
+
+ // Called only from writeObject, to ensure compatible ordering.
+ void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
+ Node<K,V>[] tab;
+ if (size > 0 && (tab = table) != null) {
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next) {
+ s.writeObject(e.key);
+ s.writeObject(e.value);
+ }
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ // Tree bins
+
+ /**
+ * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
+ * extends Node) so can be used as extension of either regular or
+ * linked node.
+ */
+ static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
+ TreeNode<K,V> parent; // red-black tree links
+ TreeNode<K,V> left;
+ TreeNode<K,V> right;
+ TreeNode<K,V> prev; // needed to unlink next upon deletion
+ boolean red;
+ TreeNode(int hash, K key, V val, Node<K,V> next) {
+ super(hash, key, val, next);
+ }
+
+ /**
+ * Returns root of tree containing this node.
+ */
+ final TreeNode<K,V> root() {
+ for (TreeNode<K,V> r = this, p;;) {
+ if ((p = r.parent) == null)
+ return r;
+ r = p;
+ }
+ }
+
+ /**
+ * Ensures that the given root is the first node of its bin.
+ */
+ static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
+ int n;
+ if (root != null && tab != null && (n = tab.length) > 0) {
+ int index = (n - 1) & root.hash;
+ TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
+ if (root != first) {
+ Node<K,V> rn;
+ tab[index] = root;
+ TreeNode<K,V> rp = root.prev;
+ if ((rn = root.next) != null)
+ ((TreeNode<K,V>)rn).prev = rp;
+ if (rp != null)
+ rp.next = rn;
+ if (first != null)
+ first.prev = root;
+ root.next = first;
+ root.prev = null;
+ }
+ assert checkInvariants(root);
+ }
+ }
+
+ /**
+ * Finds the node starting at root p with the given hash and key.
+ * The kc argument caches comparableClassFor(key) upon first use
+ * comparing keys.
+ */
+ final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
+ TreeNode<K,V> p = this;
+ do {
+ int ph, dir; K pk;
+ TreeNode<K,V> pl = p.left, pr = p.right, q;
+ if ((ph = p.hash) > h)
+ p = pl;
+ else if (ph < h)
+ p = pr;
+ else if ((pk = p.key) == k || (k != null && k.equals(pk)))
+ return p;
+ else if (pl == null)
+ p = pr;
+ else if (pr == null)
+ p = pl;
+ else if ((kc != null ||
+ (kc = comparableClassFor(k)) != null) &&
+ (dir = compareComparables(kc, k, pk)) != 0)
+ p = (dir < 0) ? pl : pr;
+ else if ((q = pr.find(h, k, kc)) != null)
+ return q;
+ else
+ p = pl;
+ } while (p != null);
+ return null;
+ }
+
+ /**
+ * Calls find for root node.
+ */
+ final TreeNode<K,V> getTreeNode(int h, Object k) {
+ return ((parent != null) ? root() : this).find(h, k, null);
+ }
+
+ /**
+ * Tie-breaking utility for ordering insertions when equal
+ * hashCodes and non-comparable. We don't require a total
+ * order, just a consistent insertion rule to maintain
+ * equivalence across rebalancings. Tie-breaking further than
+ * necessary simplifies testing a bit.
+ */
+ static int tieBreakOrder(Object a, Object b) {
+ int d;
+ if (a == null || b == null ||
+ (d = a.getClass().getName().
+ compareTo(b.getClass().getName())) == 0)
+ d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
+ -1 : 1);
+ return d;
+ }
+
+ /**
+ * Forms tree of the nodes linked from this node.
+ * @return root of tree
+ */
+ final void treeify(Node<K,V>[] tab) {
+ TreeNode<K,V> root = null;
+ for (TreeNode<K,V> x = this, next; x != null; x = next) {
+ next = (TreeNode<K,V>)x.next;
+ x.left = x.right = null;
+ if (root == null) {
+ x.parent = null;
+ x.red = false;
+ root = x;
+ }
+ else {
+ K k = x.key;
+ int h = x.hash;
+ Class<?> kc = null;
+ for (TreeNode<K,V> p = root;;) {
+ int dir, ph;
+ K pk = p.key;
+ if ((ph = p.hash) > h)
+ dir = -1;
+ else if (ph < h)
+ dir = 1;
+ else if ((kc == null &&
+ (kc = comparableClassFor(k)) == null) ||
+ (dir = compareComparables(kc, k, pk)) == 0)
+ dir = tieBreakOrder(k, pk);
+
+ TreeNode<K,V> xp = p;
+ if ((p = (dir <= 0) ? p.left : p.right) == null) {
+ x.parent = xp;
+ if (dir <= 0)
+ xp.left = x;
+ else
+ xp.right = x;
+ root = balanceInsertion(root, x);
+ break;
+ }
+ }
+ }
+ }
+ moveRootToFront(tab, root);
+ }
+
+ /**
+ * Returns a list of non-TreeNodes replacing those linked from
+ * this node.
+ */
+ final Node<K,V> untreeify(HashMap<K,V> map) {
+ Node<K,V> hd = null, tl = null;
+ for (Node<K,V> q = this; q != null; q = q.next) {
+ Node<K,V> p = map.replacementNode(q, null);
+ if (tl == null)
+ hd = p;
+ else
+ tl.next = p;
+ tl = p;
+ }
+ return hd;
+ }
+
+ /**
+ * Tree version of putVal.
+ */
+ final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
+ int h, K k, V v) {
+ Class<?> kc = null;
+ boolean searched = false;
+ TreeNode<K,V> root = (parent != null) ? root() : this;
+ for (TreeNode<K,V> p = root;;) {
+ int dir, ph; K pk;
+ if ((ph = p.hash) > h)
+ dir = -1;
+ else if (ph < h)
+ dir = 1;
+ else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
+ return p;
+ else if ((kc == null &&
+ (kc = comparableClassFor(k)) == null) ||
+ (dir = compareComparables(kc, k, pk)) == 0) {
+ if (!searched) {
+ TreeNode<K,V> q, ch;
+ searched = true;
+ if (((ch = p.left) != null &&
+ (q = ch.find(h, k, kc)) != null) ||
+ ((ch = p.right) != null &&
+ (q = ch.find(h, k, kc)) != null))
+ return q;
+ }
+ dir = tieBreakOrder(k, pk);
+ }
+
+ TreeNode<K,V> xp = p;
+ if ((p = (dir <= 0) ? p.left : p.right) == null) {
+ Node<K,V> xpn = xp.next;
+ TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
+ if (dir <= 0)
+ xp.left = x;
+ else
+ xp.right = x;
+ xp.next = x;
+ x.parent = x.prev = xp;
+ if (xpn != null)
+ ((TreeNode<K,V>)xpn).prev = x;
+ moveRootToFront(tab, balanceInsertion(root, x));
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Removes the given node, that must be present before this call.
+ * This is messier than typical red-black deletion code because we
+ * cannot swap the contents of an interior node with a leaf
+ * successor that is pinned by "next" pointers that are accessible
+ * independently during traversal. So instead we swap the tree
+ * linkages. If the current tree appears to have too few nodes,
+ * the bin is converted back to a plain bin. (The test triggers
+ * somewhere between 2 and 6 nodes, depending on tree structure).
+ */
+ final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
+ boolean movable) {
+ int n;
+ if (tab == null || (n = tab.length) == 0)
+ return;
+ int index = (n - 1) & hash;
+ TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
+ TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
+ if (pred == null)
+ tab[index] = first = succ;
+ else
+ pred.next = succ;
+ if (succ != null)
+ succ.prev = pred;
+ if (first == null)
+ return;
+ if (root.parent != null)
+ root = root.root();
+ if (root == null || root.right == null ||
+ (rl = root.left) == null || rl.left == null) {
+ tab[index] = first.untreeify(map); // too small
+ return;
+ }
+ TreeNode<K,V> p = this, pl = left, pr = right, replacement;
+ if (pl != null && pr != null) {
+ TreeNode<K,V> s = pr, sl;
+ while ((sl = s.left) != null) // find successor
+ s = sl;
+ boolean c = s.red; s.red = p.red; p.red = c; // swap colors
+ TreeNode<K,V> sr = s.right;
+ TreeNode<K,V> pp = p.parent;
+ if (s == pr) { // p was s's direct parent
+ p.parent = s;
+ s.right = p;
+ }
+ else {
+ TreeNode<K,V> sp = s.parent;
+ if ((p.parent = sp) != null) {
+ if (s == sp.left)
+ sp.left = p;
+ else
+ sp.right = p;
+ }
+ if ((s.right = pr) != null)
+ pr.parent = s;
+ }
+ p.left = null;
+ if ((p.right = sr) != null)
+ sr.parent = p;
+ if ((s.left = pl) != null)
+ pl.parent = s;
+ if ((s.parent = pp) == null)
+ root = s;
+ else if (p == pp.left)
+ pp.left = s;
+ else
+ pp.right = s;
+ if (sr != null)
+ replacement = sr;
+ else
+ replacement = p;
+ }
+ else if (pl != null)
+ replacement = pl;
+ else if (pr != null)
+ replacement = pr;
+ else
+ replacement = p;
+ if (replacement != p) {
+ TreeNode<K,V> pp = replacement.parent = p.parent;
+ if (pp == null)
+ root = replacement;
+ else if (p == pp.left)
+ pp.left = replacement;
+ else
+ pp.right = replacement;
+ p.left = p.right = p.parent = null;
+ }
+
+ TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
+
+ if (replacement == p) { // detach
+ TreeNode<K,V> pp = p.parent;
+ p.parent = null;
+ if (pp != null) {
+ if (p == pp.left)
+ pp.left = null;
+ else if (p == pp.right)
+ pp.right = null;
+ }
+ }
+ if (movable)
+ moveRootToFront(tab, r);
+ }
+
+ /**
+ * Splits nodes in a tree bin into lower and upper tree bins,
+ * or untreeifies if now too small. Called only from resize;
+ * see above discussion about split bits and indices.
+ *
+ * @param map the map
+ * @param tab the table for recording bin heads
+ * @param index the index of the table being split
+ * @param bit the bit of hash to split on
+ */
+ final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
+ TreeNode<K,V> b = this;
+ // Relink into lo and hi lists, preserving order
+ TreeNode<K,V> loHead = null, loTail = null;
+ TreeNode<K,V> hiHead = null, hiTail = null;
+ int lc = 0, hc = 0;
+ for (TreeNode<K,V> e = b, next; e != null; e = next) {
+ next = (TreeNode<K,V>)e.next;
+ e.next = null;
+ if ((e.hash & bit) == 0) {
+ if ((e.prev = loTail) == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ ++lc;
+ }
+ else {
+ if ((e.prev = hiTail) == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ ++hc;
+ }
+ }
+
+ if (loHead != null) {
+ if (lc <= UNTREEIFY_THRESHOLD)
+ tab[index] = loHead.untreeify(map);
+ else {
+ tab[index] = loHead;
+ if (hiHead != null) // (else is already treeified)
+ loHead.treeify(tab);
+ }
+ }
+ if (hiHead != null) {
+ if (hc <= UNTREEIFY_THRESHOLD)
+ tab[index + bit] = hiHead.untreeify(map);
+ else {
+ tab[index + bit] = hiHead;
+ if (loHead != null)
+ hiHead.treeify(tab);
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ // Red-black tree methods, all adapted from CLR
+
+ static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
+ TreeNode<K,V> p) {
+ TreeNode<K,V> r, pp, rl;
+ if (p != null && (r = p.right) != null) {
+ if ((rl = p.right = r.left) != null)
+ rl.parent = p;
+ if ((pp = r.parent = p.parent) == null)
+ (root = r).red = false;
+ else if (pp.left == p)
+ pp.left = r;
+ else
+ pp.right = r;
+ r.left = p;
+ p.parent = r;
+ }
+ return root;
+ }
+
+ static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
+ TreeNode<K,V> p) {
+ TreeNode<K,V> l, pp, lr;
+ if (p != null && (l = p.left) != null) {
+ if ((lr = p.left = l.right) != null)
+ lr.parent = p;
+ if ((pp = l.parent = p.parent) == null)
+ (root = l).red = false;
+ else if (pp.right == p)
+ pp.right = l;
+ else
+ pp.left = l;
+ l.right = p;
+ p.parent = l;
+ }
+ return root;
+ }
+
+ static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
+ TreeNode<K,V> x) {
+ x.red = true;
+ for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
+ if ((xp = x.parent) == null) {
+ x.red = false;
+ return x;
+ }
+ else if (!xp.red || (xpp = xp.parent) == null)
+ return root;
+ if (xp == (xppl = xpp.left)) {
+ if ((xppr = xpp.right) != null && xppr.red) {
+ xppr.red = false;
+ xp.red = false;
+ xpp.red = true;
+ x = xpp;
+ }
+ else {
+ if (x == xp.right) {
+ root = rotateLeft(root, x = xp);
+ xpp = (xp = x.parent) == null ? null : xp.parent;
+ }
+ if (xp != null) {
+ xp.red = false;
+ if (xpp != null) {
+ xpp.red = true;
+ root = rotateRight(root, xpp);
+ }
+ }
+ }
+ }
+ else {
+ if (xppl != null && xppl.red) {
+ xppl.red = false;
+ xp.red = false;
+ xpp.red = true;
+ x = xpp;
+ }
+ else {
+ if (x == xp.left) {
+ root = rotateRight(root, x = xp);
+ xpp = (xp = x.parent) == null ? null : xp.parent;
+ }
+ if (xp != null) {
+ xp.red = false;
+ if (xpp != null) {
+ xpp.red = true;
+ root = rotateLeft(root, xpp);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
+ TreeNode<K,V> x) {
+ for (TreeNode<K,V> xp, xpl, xpr;;) {
+ if (x == null || x == root)
+ return root;
+ else if ((xp = x.parent) == null) {
+ x.red = false;
+ return x;
+ }
+ else if (x.red) {
+ x.red = false;
+ return root;
+ }
+ else if ((xpl = xp.left) == x) {
+ if ((xpr = xp.right) != null && xpr.red) {
+ xpr.red = false;
+ xp.red = true;
+ root = rotateLeft(root, xp);
+ xpr = (xp = x.parent) == null ? null : xp.right;
+ }
+ if (xpr == null)
+ x = xp;
+ else {
+ TreeNode<K,V> sl = xpr.left, sr = xpr.right;
+ if ((sr == null || !sr.red) &&
+ (sl == null || !sl.red)) {
+ xpr.red = true;
+ x = xp;
+ }
+ else {
+ if (sr == null || !sr.red) {
+ if (sl != null)
+ sl.red = false;
+ xpr.red = true;
+ root = rotateRight(root, xpr);
+ xpr = (xp = x.parent) == null ?
+ null : xp.right;
+ }
+ if (xpr != null) {
+ xpr.red = (xp == null) ? false : xp.red;
+ if ((sr = xpr.right) != null)
+ sr.red = false;
+ }
+ if (xp != null) {
+ xp.red = false;
+ root = rotateLeft(root, xp);
+ }
+ x = root;
+ }
+ }
+ }
+ else { // symmetric
+ if (xpl != null && xpl.red) {
+ xpl.red = false;
+ xp.red = true;
+ root = rotateRight(root, xp);
+ xpl = (xp = x.parent) == null ? null : xp.left;
+ }
+ if (xpl == null)
+ x = xp;
+ else {
+ TreeNode<K,V> sl = xpl.left, sr = xpl.right;
+ if ((sl == null || !sl.red) &&
+ (sr == null || !sr.red)) {
+ xpl.red = true;
+ x = xp;
+ }
+ else {
+ if (sl == null || !sl.red) {
+ if (sr != null)
+ sr.red = false;
+ xpl.red = true;
+ root = rotateLeft(root, xpl);
+ xpl = (xp = x.parent) == null ?
+ null : xp.left;
+ }
+ if (xpl != null) {
+ xpl.red = (xp == null) ? false : xp.red;
+ if ((sl = xpl.left) != null)
+ sl.red = false;
+ }
+ if (xp != null) {
+ xp.red = false;
+ root = rotateRight(root, xp);
+ }
+ x = root;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Recursive invariant check
+ */
+ static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
+ TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
+ tb = t.prev, tn = (TreeNode<K,V>)t.next;
+ if (tb != null && tb.next != t)
+ return false;
+ if (tn != null && tn.prev != t)
+ return false;
+ if (tp != null && t != tp.left && t != tp.right)
+ return false;
+ if (tl != null && (tl.parent != t || tl.hash > t.hash))
+ return false;
+ if (tr != null && (tr.parent != t || tr.hash < t.hash))
+ return false;
+ if (t.red && tl != null && tl.red && tr != null && tr.red)
+ return false;
+ if (tl != null && !checkInvariants(tl))
+ return false;
+ if (tr != null && !checkInvariants(tr))
+ return false;
+ return true;
+ }
+ }
+
}
--- a/jdk/src/share/classes/java/util/LinkedHashMap.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/LinkedHashMap.java Fri Sep 06 12:10:30 2013 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -24,9 +24,12 @@
*/
package java.util;
-import java.io.*;
+
+import java.util.function.Consumer;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
+import java.io.Serializable;
+import java.io.IOException;
/**
* <p>Hash table and linked list implementation of the <tt>Map</tt> interface,
@@ -57,9 +60,9 @@
* order they were presented.)
*
* <p>A special {@link #LinkedHashMap(int,float,boolean) constructor} is
- * provided to create a <tt>LinkedHashMap</tt> whose order of iteration is the
- * order in which its entries were last accessed, from least-recently accessed
- * to most-recently (<i>access-order</i>). This kind of map is well-suited to
+ * provided to create a linked hash map whose order of iteration is the order
+ * in which its entries were last accessed, from least-recently accessed to
+ * most-recently (<i>access-order</i>). This kind of map is well-suited to
* building LRU caches. Invoking the <tt>put</tt> or <tt>get</tt> method
* results in an access to the corresponding entry (assuming it exists after
* the invocation completes). The <tt>putAll</tt> method generates one entry
@@ -155,18 +158,53 @@
* @see Hashtable
* @since 1.4
*/
-
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
{
+ /*
+ * Implementation note. A previous version of this class was
+ * internally structured a little differently. Because superclass
+ * HashMap now uses trees for some of its nodes, class
+ * LinkedHashMap.Entry is now treated as intermediary node class
+ * that can also be converted to tree form. The name of this
+ * class, LinkedHashMap.Entry, is confusing in several ways in its
+ * current context, but cannot be changed. Otherwise, even though
+ * it is not exported outside this package, some existing source
+ * code is known to have relied on a symbol resolution corner case
+ * rule in calls to removeEldestEntry that suppressed compilation
+ * errors due to ambiguous usages. So, we keep the name to
+ * preserve unmodified compilability.
+ *
+ * The changes in node classes also require using two fields
+ * (head, tail) rather than a pointer to a header node to maintain
+ * the doubly-linked before/after list. This class also
+ * previously used a different style of callback methods upon
+ * access, insertion, and removal.
+ */
+
+ /**
+ * HashMap.Node subclass for normal LinkedHashMap entries.
+ */
+ static class Entry<K,V> extends HashMap.Node<K,V> {
+ Entry<K,V> before, after;
+ Entry(int hash, K key, V value, Node<K,V> next) {
+ super(hash, key, value, next);
+ }
+ }
+
private static final long serialVersionUID = 3801124242820219131L;
/**
- * The head of the doubly linked list.
+ * The head (eldest) of the doubly linked list.
*/
- private transient Entry<K,V> header;
+ transient LinkedHashMap.Entry<K,V> head;
+
+ /**
+ * The tail (youngest) of the doubly linked list.
+ */
+ transient LinkedHashMap.Entry<K,V> tail;
/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
@@ -174,7 +212,125 @@
*
* @serial
*/
- private final boolean accessOrder;
+ final boolean accessOrder;
+
+ // internal utilities
+
+ // link at the end of list
+ private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
+ LinkedHashMap.Entry<K,V> last = tail;
+ tail = p;
+ if (last == null)
+ head = p;
+ else {
+ p.before = last;
+ last.after = p;
+ }
+ }
+
+ // apply src's links to dst
+ private void transferLinks(LinkedHashMap.Entry<K,V> src,
+ LinkedHashMap.Entry<K,V> dst) {
+ LinkedHashMap.Entry<K,V> b = dst.before = src.before;
+ LinkedHashMap.Entry<K,V> a = dst.after = src.after;
+ if (b == null)
+ head = dst;
+ else
+ b.after = dst;
+ if (a == null)
+ tail = dst;
+ else
+ a.before = dst;
+ }
+
+ // overrides of HashMap hook methods
+
+ void reinitialize() {
+ super.reinitialize();
+ head = tail = null;
+ }
+
+ Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
+ LinkedHashMap.Entry<K,V> p =
+ new LinkedHashMap.Entry<K,V>(hash, key, value, e);
+ linkNodeLast(p);
+ return p;
+ }
+
+ Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
+ LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
+ LinkedHashMap.Entry<K,V> t =
+ new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);
+ transferLinks(q, t);
+ return t;
+ }
+
+ TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
+ TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
+ linkNodeLast(p);
+ return p;
+ }
+
+ TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
+ LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
+ TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
+ transferLinks(q, t);
+ return t;
+ }
+
+ void afterNodeRemoval(Node<K,V> e) { // unlink
+ LinkedHashMap.Entry<K,V> p =
+ (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
+ p.before = p.after = null;
+ if (b == null)
+ head = a;
+ else
+ b.after = a;
+ if (a == null)
+ tail = b;
+ else
+ a.before = b;
+ }
+
+ void afterNodeInsertion(boolean evict) { // possibly remove eldest
+ LinkedHashMap.Entry<K,V> first;
+ if (evict && (first = head) != null && removeEldestEntry(first)) {
+ K key = first.key;
+ removeNode(hash(key), key, null, false, true);
+ }
+ }
+
+ void afterNodeAccess(Node<K,V> e) { // move node to last
+ LinkedHashMap.Entry<K,V> last;
+ if (accessOrder && (last = tail) != e) {
+ LinkedHashMap.Entry<K,V> p =
+ (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
+ p.after = null;
+ if (b == null)
+ head = a;
+ else
+ b.after = a;
+ if (a != null)
+ a.before = b;
+ else
+ last = b;
+ if (last == null)
+ head = p;
+ else {
+ p.before = last;
+ last.after = p;
+ }
+ tail = p;
+ ++modCount;
+ }
+ }
+
+ void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
+ for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
+ s.writeObject(e.key);
+ s.writeObject(e.value);
+ }
+ }
/**
* Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
@@ -221,8 +377,9 @@
* @throws NullPointerException if the specified map is null
*/
public LinkedHashMap(Map<? extends K, ? extends V> m) {
- super(m);
+ super();
accessOrder = false;
+ putMapEntries(m, false);
}
/**
@@ -243,16 +400,6 @@
this.accessOrder = accessOrder;
}
- /**
- * Called by superclass constructors and pseudoconstructors (clone,
- * readObject) before any entries are inserted into the map. Initializes
- * the chain.
- */
- @Override
- void init() {
- header = new Entry<>(-1, null, null, null);
- header.before = header.after = header;
- }
/**
* Returns <tt>true</tt> if this map maps one or more keys to the
@@ -263,15 +410,10 @@
* specified value
*/
public boolean containsValue(Object value) {
- // Overridden to take advantage of faster iterator
- if (value==null) {
- for (Entry<?,?> e = header.after; e != header; e = e.after)
- if (e.value==null)
- return true;
- } else {
- for (Entry<?,?> e = header.after; e != header; e = e.after)
- if (value.equals(e.value))
- return true;
+ for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
+ V v = e.value;
+ if (v == value || (value != null && value.equals(v)))
+ return true;
}
return false;
}
@@ -292,10 +434,11 @@
* distinguish these two cases.
*/
public V get(Object key) {
- Entry<K,V> e = (Entry<K,V>)getEntry(key);
- if (e == null)
+ Node<K,V> e;
+ if ((e = getNode(hash(key), key)) == null)
return null;
- e.recordAccess(this);
+ if (accessOrder)
+ afterNodeAccess(e);
return e.value;
}
@@ -305,163 +448,7 @@
*/
public void clear() {
super.clear();
- header.before = header.after = header;
- }
-
- @Override
- public void forEach(BiConsumer<? super K, ? super V> action) {
- Objects.requireNonNull(action);
- int expectedModCount = modCount;
- for (Entry<K, V> entry = header.after; entry != header; entry = entry.after) {
- action.accept(entry.key, entry.value);
-
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
- }
-
- @Override
- public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
- Objects.requireNonNull(function);
- int expectedModCount = modCount;
- for (Entry<K, V> entry = header.after; entry != header; entry = entry.after) {
- entry.value = function.apply(entry.key, entry.value);
-
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- }
- }
-
- /**
- * LinkedHashMap entry.
- */
- private static class Entry<K,V> extends HashMap.Entry<K,V> {
- // These fields comprise the doubly linked list used for iteration.
- Entry<K,V> before, after;
-
- Entry(int hash, K key, V value, Object next) {
- super(hash, key, value, next);
- }
-
- /**
- * Removes this entry from the linked list.
- */
- private void remove() {
- before.after = after;
- after.before = before;
- }
-
- /**
- * Inserts this entry before the specified existing entry in the list.
- */
- private void addBefore(Entry<K,V> existingEntry) {
- after = existingEntry;
- before = existingEntry.before;
- before.after = this;
- after.before = this;
- }
-
- /**
- * This method is invoked by the superclass whenever the value
- * of a pre-existing entry is read by Map.get or modified by Map.put.
- * If the enclosing Map is access-ordered, it moves the entry
- * to the end of the list; otherwise, it does nothing.
- */
- void recordAccess(HashMap<K,V> m) {
- LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
- if (lm.accessOrder) {
- lm.modCount++;
- remove();
- addBefore(lm.header);
- }
- }
-
- void recordRemoval(HashMap<K,V> m) {
- remove();
- }
- }
-
- private abstract class LinkedHashIterator<T> implements Iterator<T> {
- Entry<K,V> nextEntry = header.after;
- Entry<K,V> lastReturned = null;
-
- /**
- * The modCount value that the iterator believes that the backing
- * List should have. If this expectation is violated, the iterator
- * has detected concurrent modification.
- */
- int expectedModCount = modCount;
-
- public boolean hasNext() {
- return nextEntry != header;
- }
-
- public void remove() {
- if (lastReturned == null)
- throw new IllegalStateException();
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
-
- LinkedHashMap.this.remove(lastReturned.key);
- lastReturned = null;
- expectedModCount = modCount;
- }
-
- Entry<K,V> nextEntry() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- if (nextEntry == header)
- throw new NoSuchElementException();
-
- Entry<K,V> e = lastReturned = nextEntry;
- nextEntry = e.after;
- return e;
- }
- }
-
- private class KeyIterator extends LinkedHashIterator<K> {
- public K next() { return nextEntry().getKey(); }
- }
-
- private class ValueIterator extends LinkedHashIterator<V> {
- public V next() { return nextEntry().value; }
- }
-
- private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
- public Map.Entry<K,V> next() { return nextEntry(); }
- }
-
- // These Overrides alter the behavior of superclass view iterator() methods
- Iterator<K> newKeyIterator() { return new KeyIterator(); }
- Iterator<V> newValueIterator() { return new ValueIterator(); }
- Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }
-
- /**
- * This override alters behavior of superclass put method. It causes newly
- * allocated entry to get inserted at the end of the linked list and
- * removes the eldest entry if appropriate.
- */
- @Override
- void addEntry(int hash, K key, V value, int bucketIndex, boolean checkIfNeedTree) {
- super.addEntry(hash, key, value, bucketIndex, checkIfNeedTree);
-
- // Remove eldest entry if instructed
- Entry<K,V> eldest = header.after;
- if (removeEldestEntry(eldest)) {
- removeEntryForKey(eldest.key);
- }
- }
-
- /*
- * Create a new LinkedHashMap.Entry and setup the before/after pointers
- */
- @Override
- HashMap.Entry<K,V> newEntry(int hash, K key, V value, Object next) {
- Entry<K,V> newEntry = new Entry<>(hash, key, value, next);
- newEntry.addBefore(header);
- return newEntry;
+ head = tail = null;
}
/**
@@ -475,13 +462,13 @@
* <p>Sample use: this override will allow the map to grow up to 100
* entries and then delete the eldest entry each time a new entry is
* added, maintaining a steady state of 100 entries.
- * <pre>{@code
+ * <pre>
* private static final int MAX_ENTRIES = 100;
*
* protected boolean removeEldestEntry(Map.Entry eldest) {
- * return size() > MAX_ENTRIES;
+ * return size() > MAX_ENTRIES;
* }
- * }</pre>
+ * </pre>
*
* <p>This method typically does not modify the map in any way,
* instead allowing the map to modify itself as directed by its
@@ -508,4 +495,241 @@
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
+
+ /**
+ * Returns a {@link Set} view of the keys contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa. If the map is modified
+ * while an iteration over the set is in progress (except through
+ * the iterator's own <tt>remove</tt> operation), the results of
+ * the iteration are undefined. The set supports element removal,
+ * which removes the corresponding mapping from the map, via the
+ * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
+ * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
+ * operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
+ * operations.
+ * Its {@link Spliterator} typically provides faster sequential
+ * performance but much poorer parallel performance than that of
+ * {@code HashMap}.
+ *
+ * @return a set view of the keys contained in this map
+ */
+ public Set<K> keySet() {
+ Set<K> ks;
+ return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks;
+ }
+
+ final class LinkedKeySet extends AbstractSet<K> {
+ public final int size() { return size; }
+ public final void clear() { LinkedHashMap.this.clear(); }
+ public final Iterator<K> iterator() {
+ return new LinkedKeyIterator();
+ }
+ public final boolean contains(Object o) { return containsKey(o); }
+ public final boolean remove(Object key) {
+ return removeNode(hash(key), key, null, false, true) != null;
+ }
+ public final Spliterator<K> spliterator() {
+ return Spliterators.spliterator(this, Spliterator.SIZED |
+ Spliterator.ORDERED |
+ Spliterator.DISTINCT);
+ }
+ public final void forEach(Consumer<? super K> action) {
+ if (action == null)
+ throw new NullPointerException();
+ int mc = modCount;
+ for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
+ action.accept(e.key);
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ /**
+ * Returns a {@link Collection} view of the values contained in this map.
+ * The collection is backed by the map, so changes to the map are
+ * reflected in the collection, and vice-versa. If the map is
+ * modified while an iteration over the collection is in progress
+ * (except through the iterator's own <tt>remove</tt> operation),
+ * the results of the iteration are undefined. The collection
+ * supports element removal, which removes the corresponding
+ * mapping from the map, via the <tt>Iterator.remove</tt>,
+ * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
+ * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
+ * support the <tt>add</tt> or <tt>addAll</tt> operations.
+ * Its {@link Spliterator} typically provides faster sequential
+ * performance but much poorer parallel performance than that of
+ * {@code HashMap}.
+ *
+ * @return a view of the values contained in this map
+ */
+ public Collection<V> values() {
+ Collection<V> vs;
+ return (vs = values) == null ? (values = new LinkedValues()) : vs;
+ }
+
+ final class LinkedValues extends AbstractCollection<V> {
+ public final int size() { return size; }
+ public final void clear() { LinkedHashMap.this.clear(); }
+ public final Iterator<V> iterator() {
+ return new LinkedValueIterator();
+ }
+ public final boolean contains(Object o) { return containsValue(o); }
+ public final Spliterator<V> spliterator() {
+ return Spliterators.spliterator(this, Spliterator.SIZED |
+ Spliterator.ORDERED);
+ }
+ public final void forEach(Consumer<? super V> action) {
+ if (action == null)
+ throw new NullPointerException();
+ int mc = modCount;
+ for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
+ action.accept(e.value);
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ /**
+ * Returns a {@link Set} view of the mappings contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa. If the map is modified
+ * while an iteration over the set is in progress (except through
+ * the iterator's own <tt>remove</tt> operation, or through the
+ * <tt>setValue</tt> operation on a map entry returned by the
+ * iterator) the results of the iteration are undefined. The set
+ * supports element removal, which removes the corresponding
+ * mapping from the map, via the <tt>Iterator.remove</tt>,
+ * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
+ * <tt>clear</tt> operations. It does not support the
+ * <tt>add</tt> or <tt>addAll</tt> operations.
+ * Its {@link Spliterator} typically provides faster sequential
+ * performance but much poorer parallel performance than that of
+ * {@code HashMap}.
+ *
+ * @return a set view of the mappings contained in this map
+ */
+ public Set<Map.Entry<K,V>> entrySet() {
+ Set<Map.Entry<K,V>> es;
+ return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
+ }
+
+ final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
+ public final int size() { return size; }
+ public final void clear() { LinkedHashMap.this.clear(); }
+ public final Iterator<Map.Entry<K,V>> iterator() {
+ return new LinkedEntryIterator();
+ }
+ public final boolean contains(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+ Object key = e.getKey();
+ Node<K,V> candidate = getNode(hash(key), key);
+ return candidate != null && candidate.equals(e);
+ }
+ public final boolean remove(Object o) {
+ if (o instanceof Map.Entry) {
+ Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+ Object key = e.getKey();
+ Object value = e.getValue();
+ return removeNode(hash(key), key, value, true, true) != null;
+ }
+ return false;
+ }
+ public final Spliterator<Map.Entry<K,V>> spliterator() {
+ return Spliterators.spliterator(this, Spliterator.SIZED |
+ Spliterator.ORDERED |
+ Spliterator.DISTINCT);
+ }
+ public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
+ if (action == null)
+ throw new NullPointerException();
+ int mc = modCount;
+ for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
+ action.accept(e);
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ // Map overrides
+
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ if (action == null)
+ throw new NullPointerException();
+ int mc = modCount;
+ for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
+ action.accept(e.key, e.value);
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+
+ public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+ if (function == null)
+ throw new NullPointerException();
+ int mc = modCount;
+ for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
+ e.value = function.apply(e.key, e.value);
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+
+ // Iterators
+
+ abstract class LinkedHashIterator {
+ LinkedHashMap.Entry<K,V> next;
+ LinkedHashMap.Entry<K,V> current;
+ int expectedModCount;
+
+ LinkedHashIterator() {
+ next = head;
+ expectedModCount = modCount;
+ current = null;
+ }
+
+ public final boolean hasNext() {
+ return next != null;
+ }
+
+ final LinkedHashMap.Entry<K,V> nextNode() {
+ LinkedHashMap.Entry<K,V> e = next;
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (e == null)
+ throw new NoSuchElementException();
+ current = e;
+ next = e.after;
+ return e;
+ }
+
+ public final void remove() {
+ Node<K,V> p = current;
+ if (p == null)
+ throw new IllegalStateException();
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ current = null;
+ K key = p.key;
+ removeNode(hash(key), key, null, false, false);
+ expectedModCount = modCount;
+ }
+ }
+
+ final class LinkedKeyIterator extends LinkedHashIterator
+ implements Iterator<K> {
+ public final K next() { return nextNode().getKey(); }
+ }
+
+ final class LinkedValueIterator extends LinkedHashIterator
+ implements Iterator<V> {
+ public final V next() { return nextNode().value; }
+ }
+
+ final class LinkedEntryIterator extends LinkedHashIterator
+ implements Iterator<Map.Entry<K,V>> {
+ public final Map.Entry<K,V> next() { return nextNode(); }
+ }
+
+
}
--- a/jdk/src/share/classes/java/util/Random.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/Random.java Fri Sep 06 12:10:30 2013 -0400
@@ -26,9 +26,13 @@
package java.util;
import java.io.*;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
+import java.util.stream.StreamSupport;
import sun.misc.Unsafe;
@@ -85,6 +89,13 @@
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
+ private static final double DOUBLE_UNIT = 1.0 / (1L << 53);
+
+ // IllegalArgumentException messages
+ static final String BadBound = "bound must be positive";
+ static final String BadRange = "bound must be greater than origin";
+ static final String BadSize = "size must be non-negative";
+
/**
* Creates a new random number generator. This constructor sets
* the seed of the random number generator to a value very likely
@@ -222,6 +233,82 @@
}
/**
+ * The form of nextLong used by LongStream Spliterators. If
+ * origin is greater than bound, acts as unbounded form of
+ * nextLong, else as bounded form.
+ *
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final long internalNextLong(long origin, long bound) {
+ long r = nextLong();
+ if (origin < bound) {
+ long n = bound - origin, m = n - 1;
+ if ((n & m) == 0L) // power of two
+ r = (r & m) + origin;
+ else if (n > 0L) { // reject over-represented candidates
+ for (long u = r >>> 1; // ensure nonnegative
+ u + m - (r = u % n) < 0L; // rejection check
+ u = nextLong() >>> 1) // retry
+ ;
+ r += origin;
+ }
+ else { // range not representable as long
+ while (r < origin || r >= bound)
+ r = nextLong();
+ }
+ }
+ return r;
+ }
+
+ /**
+ * The form of nextInt used by IntStream Spliterators.
+ * For the unbounded case: uses nextInt().
+ * For the bounded case with representable range: uses nextInt(int bound)
+ * For the bounded case with unrepresentable range: uses nextInt()
+ *
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final int internalNextInt(int origin, int bound) {
+ if (origin < bound) {
+ int n = bound - origin;
+ if (n > 0) {
+ return nextInt(n) + origin;
+ }
+ else { // range not representable as int
+ int r;
+ do {
+ r = nextInt();
+ } while (r < origin || r >= bound);
+ return r;
+ }
+ }
+ else {
+ return nextInt();
+ }
+ }
+
+ /**
+ * The form of nextDouble used by DoubleStream Spliterators.
+ *
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final double internalNextDouble(double origin, double bound) {
+ double r = nextDouble();
+ if (origin < bound) {
+ r = r * (bound - origin) + origin;
+ if (r >= bound) // correct for rounding
+ r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
+ }
+ return r;
+ }
+
+ /**
* Returns the next pseudorandom, uniformly distributed {@code int}
* value from this random number generator's sequence. The general
* contract of {@code nextInt} is that one {@code int} value is
@@ -247,23 +334,23 @@
* between 0 (inclusive) and the specified value (exclusive), drawn from
* this random number generator's sequence. The general contract of
* {@code nextInt} is that one {@code int} value in the specified range
- * is pseudorandomly generated and returned. All {@code n} possible
+ * is pseudorandomly generated and returned. All {@code bound} possible
* {@code int} values are produced with (approximately) equal
- * probability. The method {@code nextInt(int n)} is implemented by
+ * probability. The method {@code nextInt(int bound)} is implemented by
* class {@code Random} as if by:
* <pre> {@code
- * public int nextInt(int n) {
- * if (n <= 0)
- * throw new IllegalArgumentException("n must be positive");
+ * public int nextInt(int bound) {
+ * if (bound <= 0)
+ * throw new IllegalArgumentException("bound must be positive");
*
- * if ((n & -n) == n) // i.e., n is a power of 2
- * return (int)((n * (long)next(31)) >> 31);
+ * if ((bound & -bound) == bound) // i.e., bound is a power of 2
+ * return (int)((bound * (long)next(31)) >> 31);
*
* int bits, val;
* do {
* bits = next(31);
- * val = bits % n;
- * } while (bits - val + (n-1) < 0);
+ * val = bits % bound;
+ * } while (bits - val + (bound-1) < 0);
* return val;
* }}</pre>
*
@@ -289,28 +376,28 @@
* greatly increases the length of the sequence of values returned by
* successive calls to this method if n is a small power of two.
*
- * @param n the bound on the random number to be returned. Must be
- * positive.
+ * @param bound the upper bound (exclusive). Must be positive.
* @return the next pseudorandom, uniformly distributed {@code int}
- * value between {@code 0} (inclusive) and {@code n} (exclusive)
+ * value between zero (inclusive) and {@code bound} (exclusive)
* from this random number generator's sequence
- * @throws IllegalArgumentException if n is not positive
+ * @throws IllegalArgumentException if bound is not positive
* @since 1.2
*/
-
- public int nextInt(int n) {
- if (n <= 0)
- throw new IllegalArgumentException("n must be positive");
+ public int nextInt(int bound) {
+ if (bound <= 0)
+ throw new IllegalArgumentException(BadBound);
- if ((n & -n) == n) // i.e., n is a power of 2
- return (int)((n * (long)next(31)) >> 31);
-
- int bits, val;
- do {
- bits = next(31);
- val = bits % n;
- } while (bits - val + (n-1) < 0);
- return val;
+ int r = next(31);
+ int m = bound - 1;
+ if ((bound & m) == 0) // i.e., bound is a power of 2
+ r = (int)((bound * (long)r) >> 31);
+ else {
+ for (int u = r;
+ u - (r = u % bound) + m < 0;
+ u = next(31))
+ ;
+ }
+ return r;
}
/**
@@ -442,8 +529,7 @@
* @see Math#random
*/
public double nextDouble() {
- return (((long)(next(26)) << 27) + next(27))
- / (double)(1L << 53);
+ return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT;
}
private double nextNextGaussian;
@@ -513,57 +599,563 @@
}
}
+ // stream methods, coded in a way intended to better isolate for
+ // maintenance purposes the small differences across forms.
+
/**
- * Returns a stream of pseudorandom, uniformly distributed
- * {@code integer} values from this random number generator's
- * sequence. Values are obtained as needed by calling
- * {@link #nextInt()}.
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code int} values.
+ *
+ * <p>A pseudorandom {@code int} value is generated as if it's the result of
+ * calling the method {@link #nextInt()}.
*
- * @return an infinite stream of {@code integer} values
+ * @param streamSize the number of values to generate
+ * @return a stream of pseudorandom {@code int} values
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ * @since 1.8
+ */
+ public IntStream ints(long streamSize) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (this, 0L, streamSize, Integer.MAX_VALUE, 0),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code int}
+ * values.
+ *
+ * <p>A pseudorandom {@code int} value is generated as if it's the result of
+ * calling the method {@link #nextInt()}.
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * ints(Long.MAX_VALUE)}.
+ *
+ * @return a stream of pseudorandom {@code int} values
* @since 1.8
*/
public IntStream ints() {
- return IntStream.generate(this::nextInt);
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (this, 0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number
+ * of pseudorandom {@code int} values, each conforming to the given
+ * origin (inclusive) and bound (exclusive).
+ *
+ * <p>A pseudorandom {@code int} value is generated as if it's the result of
+ * calling the following method with the origin and bound:
+ * <pre> {@code
+ * int nextInt(int origin, int bound) {
+ * int n = bound - origin;
+ * if (n > 0) {
+ * return nextInt(n) + origin;
+ * }
+ * else { // range not representable as int
+ * int r;
+ * do {
+ * r = nextInt();
+ * } while (r < origin || r >= bound);
+ * return r;
+ * }
+ * }}</pre>
+ *
+ * @param streamSize the number of values to generate
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code int} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero, or {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public IntStream ints(long streamSize, int randomNumberOrigin,
+ int randomNumberBound) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
+ false);
}
/**
- * Returns a stream of pseudorandom, uniformly distributed
- * {@code long} values from this random number generator's
- * sequence. Values are obtained as needed by calling
- * {@link #nextLong()}.
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * int} values, each conforming to the given origin (inclusive) and bound
+ * (exclusive).
+ *
+ * <p>A pseudorandom {@code int} value is generated as if it's the result of
+ * calling the following method with the origin and bound:
+ * <pre> {@code
+ * int nextInt(int origin, int bound) {
+ * int n = bound - origin;
+ * if (n > 0) {
+ * return nextInt(n) + origin;
+ * }
+ * else { // range not representable as int
+ * int r;
+ * do {
+ * r = nextInt();
+ * } while (r < origin || r >= bound);
+ * return r;
+ * }
+ * }}</pre>
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
*
- * @return an infinite stream of {@code long} values
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code int} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public IntStream ints(int randomNumberOrigin, int randomNumberBound) {
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code long} values.
+ *
+ * <p>A pseudorandom {@code long} value is generated as if it's the result
+ * of calling the method {@link #nextLong()}.
+ *
+ * @param streamSize the number of values to generate
+ * @return a stream of pseudorandom {@code long} values
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ * @since 1.8
+ */
+ public LongStream longs(long streamSize) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (this, 0L, streamSize, Long.MAX_VALUE, 0L),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code long}
+ * values.
+ *
+ * <p>A pseudorandom {@code long} value is generated as if it's the result
+ * of calling the method {@link #nextLong()}.
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * longs(Long.MAX_VALUE)}.
+ *
+ * @return a stream of pseudorandom {@code long} values
* @since 1.8
*/
public LongStream longs() {
- return LongStream.generate(this::nextLong);
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (this, 0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code long}, each conforming to the given origin
+ * (inclusive) and bound (exclusive).
+ *
+ * <p>A pseudorandom {@code long} value is generated as if it's the result
+ * of calling the following method with the origin and bound:
+ * <pre> {@code
+ * long nextLong(long origin, long bound) {
+ * long r = nextLong();
+ * long n = bound - origin, m = n - 1;
+ * if ((n & m) == 0L) // power of two
+ * r = (r & m) + origin;
+ * else if (n > 0L) { // reject over-represented candidates
+ * for (long u = r >>> 1; // ensure nonnegative
+ * u + m - (r = u % n) < 0L; // rejection check
+ * u = nextLong() >>> 1) // retry
+ * ;
+ * r += origin;
+ * }
+ * else { // range not representable as long
+ * while (r < origin || r >= bound)
+ * r = nextLong();
+ * }
+ * return r;
+ * }}</pre>
+ *
+ * @param streamSize the number of values to generate
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code long} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero, or {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public LongStream longs(long streamSize, long randomNumberOrigin,
+ long randomNumberBound) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
+ false);
}
/**
- * Returns a stream of pseudorandom, uniformly distributed
- * {@code double} values between {@code 0.0} and {@code 1.0}
- * from this random number generator's sequence. Values are
- * obtained as needed by calling {@link #nextDouble()}.
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * long} values, each conforming to the given origin (inclusive) and bound
+ * (exclusive).
+ *
+ * <p>A pseudorandom {@code long} value is generated as if it's the result
+ * of calling the following method with the origin and bound:
+ * <pre> {@code
+ * long nextLong(long origin, long bound) {
+ * long r = nextLong();
+ * long n = bound - origin, m = n - 1;
+ * if ((n & m) == 0L) // power of two
+ * r = (r & m) + origin;
+ * else if (n > 0L) { // reject over-represented candidates
+ * for (long u = r >>> 1; // ensure nonnegative
+ * u + m - (r = u % n) < 0L; // rejection check
+ * u = nextLong() >>> 1) // retry
+ * ;
+ * r += origin;
+ * }
+ * else { // range not representable as long
+ * while (r < origin || r >= bound)
+ * r = nextLong();
+ * }
+ * return r;
+ * }}</pre>
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * longs(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
*
- * @return an infinite stream of {@code double} values
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code long} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public LongStream longs(long randomNumberOrigin, long randomNumberBound) {
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code double} values, each between zero
+ * (inclusive) and one (exclusive).
+ *
+ * <p>A pseudorandom {@code double} value is generated as if it's the result
+ * of calling the method {@link #nextDouble()}}.
+ *
+ * @param streamSize the number of values to generate
+ * @return a stream of {@code double} values
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ * @since 1.8
+ */
+ public DoubleStream doubles(long streamSize) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (this, 0L, streamSize, Double.MAX_VALUE, 0.0),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * double} values, each between zero (inclusive) and one
+ * (exclusive).
+ *
+ * <p>A pseudorandom {@code double} value is generated as if it's the result
+ * of calling the method {@link #nextDouble()}}.
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * doubles(Long.MAX_VALUE)}.
+ *
+ * @return a stream of pseudorandom {@code double} values
* @since 1.8
*/
public DoubleStream doubles() {
- return DoubleStream.generate(this::nextDouble);
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (this, 0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code double} values, each conforming to the given origin
+ * (inclusive) and bound (exclusive).
+ *
+ * <p>A pseudorandom {@code double} value is generated as if it's the result
+ * of calling the following method with the origin and bound:
+ * <pre> {@code
+ * double nextDouble(double origin, double bound) {
+ * double r = nextDouble();
+ * r = r * (bound - origin) + origin;
+ * if (r >= bound) // correct for rounding
+ * r = Math.nextDown(bound);
+ * return r;
+ * }}</pre>
+ *
+ * @param streamSize the number of values to generate
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code double} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public DoubleStream doubles(long streamSize, double randomNumberOrigin,
+ double randomNumberBound) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ if (!(randomNumberOrigin < randomNumberBound))
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * double} values, each conforming to the given origin (inclusive) and bound
+ * (exclusive).
+ *
+ * <p>A pseudorandom {@code double} value is generated as if it's the result
+ * of calling the following method with the origin and bound:
+ * <pre> {@code
+ * double nextDouble(double origin, double bound) {
+ * double r = nextDouble();
+ * r = r * (bound - origin) + origin;
+ * if (r >= bound) // correct for rounding
+ * r = Math.nextDown(bound);
+ * return r;
+ * }}</pre>
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * doubles(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
+ *
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code double} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) {
+ if (!(randomNumberOrigin < randomNumberBound))
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ false);
}
/**
- * Returns a stream of pseudorandom, Gaussian ("normally")
- * distributed {@code double} values with mean {@code 0.0}
- * and standard deviation {@code 1.0} from this random number
- * generator's sequence. Values are obtained as needed by
- * calling {@link #nextGaussian()}.
- *
- * @return an infinite stream of {@code double} values
- * @since 1.8
+ * Spliterator for int streams. We multiplex the four int
+ * versions into one class by treating a bound less than origin as
+ * unbounded, and also by treating "infinite" as equivalent to
+ * Long.MAX_VALUE. For splits, it uses the standard divide-by-two
+ * approach. The long and double versions of this class are
+ * identical except for types.
+ */
+ static final class RandomIntsSpliterator implements Spliterator.OfInt {
+ final Random rng;
+ long index;
+ final long fence;
+ final int origin;
+ final int bound;
+ RandomIntsSpliterator(Random rng, long index, long fence,
+ int origin, int bound) {
+ this.rng = rng; this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomIntsSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomIntsSpliterator(rng, i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(IntConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(rng.internalNextInt(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(IntConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ Random r = rng;
+ int o = origin, b = bound;
+ do {
+ consumer.accept(r.internalNextInt(o, b));
+ } while (++i < f);
+ }
+ }
+ }
+
+ /**
+ * Spliterator for long streams.
*/
- public DoubleStream gaussians() {
- return DoubleStream.generate(this::nextGaussian);
+ static final class RandomLongsSpliterator implements Spliterator.OfLong {
+ final Random rng;
+ long index;
+ final long fence;
+ final long origin;
+ final long bound;
+ RandomLongsSpliterator(Random rng, long index, long fence,
+ long origin, long bound) {
+ this.rng = rng; this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomLongsSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomLongsSpliterator(rng, i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(LongConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(rng.internalNextLong(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(LongConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ Random r = rng;
+ long o = origin, b = bound;
+ do {
+ consumer.accept(r.internalNextLong(o, b));
+ } while (++i < f);
+ }
+ }
+
+ }
+
+ /**
+ * Spliterator for double streams.
+ */
+ static final class RandomDoublesSpliterator implements Spliterator.OfDouble {
+ final Random rng;
+ long index;
+ final long fence;
+ final double origin;
+ final double bound;
+ RandomDoublesSpliterator(Random rng, long index, long fence,
+ double origin, double bound) {
+ this.rng = rng; this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomDoublesSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomDoublesSpliterator(rng, i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(DoubleConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(rng.internalNextDouble(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(DoubleConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ Random r = rng;
+ double o = origin, b = bound;
+ do {
+ consumer.accept(r.internalNextDouble(o, b));
+ } while (++i < f);
+ }
+ }
}
/**
--- a/jdk/src/share/classes/java/util/concurrent/ThreadLocalRandom.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/concurrent/ThreadLocalRandom.java Fri Sep 06 12:10:30 2013 -0400
@@ -37,11 +37,16 @@
import java.io.ObjectStreamField;
import java.util.Random;
+import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
+import java.util.stream.StreamSupport;
/**
* A random number generator isolated to the current thread. Like the
@@ -64,6 +69,10 @@
* <p>This class also provides additional commonly used bounded random
* generation methods.
*
+ * <p>Instances of {@code ThreadLocalRandom} are not cryptographically
+ * secure. Consider instead using {@link java.security.SecureRandom}
+ * in security-sensitive applications.
+ *
* @since 1.7
* @author Doug Lea
*/
@@ -85,28 +94,26 @@
* application-level overhead and footprint of most concurrent
* programs.
*
+ * Even though this class subclasses java.util.Random, it uses the
+ * same basic algorithm as java.util.SplittableRandom. (See its
+ * internal documentation for explanations, which are not repeated
+ * here.) Because ThreadLocalRandoms are not splittable
+ * though, we use only a single 64bit gamma.
+ *
* Because this class is in a different package than class Thread,
* field access methods use Unsafe to bypass access control rules.
- * The base functionality of Random methods is conveniently
- * isolated in method next(bits), that just reads and writes the
- * Thread field rather than its own field. However, to conform to
- * the requirements of the Random superclass constructor, the
- * common static ThreadLocalRandom maintains an "initialized"
- * field for the sake of rejecting user calls to setSeed while
- * still allowing a call from constructor. Note that
- * serialization is completely unnecessary because there is only a
- * static singleton. But we generate a serial form containing
- * "rnd" and "initialized" fields to ensure compatibility across
- * versions.
+ * To conform to the requirements of the Random superclass
+ * constructor, the common static ThreadLocalRandom maintains an
+ * "initialized" field for the sake of rejecting user calls to
+ * setSeed while still allowing a call from constructor. Note
+ * that serialization is completely unnecessary because there is
+ * only a static singleton. But we generate a serial form
+ * containing "rnd" and "initialized" fields to ensure
+ * compatibility across versions.
*
- * Per-thread initialization is similar to that in the no-arg
- * Random constructor, but we avoid correlation among not only
- * initial seeds of those created in different threads, but also
- * those created using class Random itself; while at the same time
- * not changing any statistical properties. So we use the same
- * underlying multiplicative sequence, but start the sequence far
- * away from the base version, and then merge (xor) current time
- * and per-thread probe bits to generate initial values.
+ * Implementations of non-core methods are mostly the same as in
+ * SplittableRandom, that were in part derived from a previous
+ * version of this class.
*
* The nextLocalGaussian ThreadLocal supports the very rarely used
* nextGaussian method by providing a holder for the second of a
@@ -115,24 +122,51 @@
* but we provide identical statistical properties.
*/
- // same constants as Random, but must be redeclared because private
- private static final long multiplier = 0x5DEECE66DL;
- private static final long addend = 0xBL;
- private static final long mask = (1L << 48) - 1;
- private static final int PROBE_INCREMENT = 0x61c88647;
-
- /** Generates the basis for per-thread initial seed values */
- private static final AtomicLong seedGenerator =
- new AtomicLong(1269533684904616924L);
-
/** Generates per-thread initialization/probe field */
private static final AtomicInteger probeGenerator =
- new AtomicInteger(0xe80f8647);
+ new AtomicInteger();
+
+ /**
+ * The next seed for default constructors.
+ */
+ private static final AtomicLong seeder =
+ new AtomicLong(mix64(System.currentTimeMillis()) ^
+ mix64(System.nanoTime()));
+
+ /**
+ * The seed increment
+ */
+ private static final long GAMMA = 0x9e3779b97f4a7c15L;
+
+ /**
+ * The increment for generating probe values
+ */
+ private static final int PROBE_INCREMENT = 0x9e3779b9;
+
+ /**
+ * The increment of seeder per new instance
+ */
+ private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
+
+ // Constants from SplittableRandom
+ private static final double DOUBLE_UNIT = 1.0 / (1L << 53);
+ private static final float FLOAT_UNIT = 1.0f / (1 << 24);
/** Rarely-used holder for the second of a pair of Gaussians */
private static final ThreadLocal<Double> nextLocalGaussian =
new ThreadLocal<Double>();
+ private static long mix64(long z) {
+ z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
+ z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L;
+ return z ^ (z >>> 33);
+ }
+
+ private static int mix32(long z) {
+ z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
+ return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32);
+ }
+
/**
* Field used only during singleton initialization.
* True when constructor completes.
@@ -155,16 +189,11 @@
* rely on (static) atomic generators to initialize the values.
*/
static final void localInit() {
- int p = probeGenerator.getAndAdd(PROBE_INCREMENT);
+ int p = probeGenerator.addAndGet(PROBE_INCREMENT);
int probe = (p == 0) ? 1 : p; // skip 0
- long current, next;
- do { // same sequence as j.u.Random but different initial value
- current = seedGenerator.get();
- next = current * 181783497276652981L;
- } while (!seedGenerator.compareAndSet(current, next));
- long r = next ^ ((long)probe << 32) ^ System.nanoTime();
+ long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
Thread t = Thread.currentThread();
- UNSAFE.putLong(t, SEED, r);
+ UNSAFE.putLong(t, SEED, seed);
UNSAFE.putInt(t, PROBE, probe);
}
@@ -191,124 +220,264 @@
throw new UnsupportedOperationException();
}
- protected int next(int bits) {
+ final long nextSeed() {
Thread t; long r; // read and update per-thread seed
- UNSAFE.putLong
- (t = Thread.currentThread(), SEED,
- r = (UNSAFE.getLong(t, SEED) * multiplier + addend) & mask);
- return (int) (r >>> (48-bits));
+ UNSAFE.putLong(t = Thread.currentThread(), SEED,
+ r = UNSAFE.getLong(t, SEED) + GAMMA);
+ return r;
+ }
+
+ // We must define this, but never use it.
+ protected int next(int bits) {
+ return (int)(mix64(nextSeed()) >>> (64 - bits));
+ }
+
+ // IllegalArgumentException messages
+ static final String BadBound = "bound must be positive";
+ static final String BadRange = "bound must be greater than origin";
+ static final String BadSize = "size must be non-negative";
+
+ /**
+ * The form of nextLong used by LongStream Spliterators. If
+ * origin is greater than bound, acts as unbounded form of
+ * nextLong, else as bounded form.
+ *
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final long internalNextLong(long origin, long bound) {
+ long r = mix64(nextSeed());
+ if (origin < bound) {
+ long n = bound - origin, m = n - 1;
+ if ((n & m) == 0L) // power of two
+ r = (r & m) + origin;
+ else if (n > 0L) { // reject over-represented candidates
+ for (long u = r >>> 1; // ensure nonnegative
+ u + m - (r = u % n) < 0L; // rejection check
+ u = mix64(nextSeed()) >>> 1) // retry
+ ;
+ r += origin;
+ }
+ else { // range not representable as long
+ while (r < origin || r >= bound)
+ r = mix64(nextSeed());
+ }
+ }
+ return r;
}
/**
- * Returns a pseudorandom, uniformly distributed value between the
- * given least value (inclusive) and bound (exclusive).
+ * The form of nextInt used by IntStream Spliterators.
+ * Exactly the same as long version, except for types.
+ *
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final int internalNextInt(int origin, int bound) {
+ int r = mix32(nextSeed());
+ if (origin < bound) {
+ int n = bound - origin, m = n - 1;
+ if ((n & m) == 0)
+ r = (r & m) + origin;
+ else if (n > 0) {
+ for (int u = r >>> 1;
+ u + m - (r = u % n) < 0;
+ u = mix32(nextSeed()) >>> 1)
+ ;
+ r += origin;
+ }
+ else {
+ while (r < origin || r >= bound)
+ r = mix32(nextSeed());
+ }
+ }
+ return r;
+ }
+
+ /**
+ * The form of nextDouble used by DoubleStream Spliterators.
*
- * @param least the least value returned
- * @param bound the upper bound (exclusive)
- * @throws IllegalArgumentException if least greater than or equal
- * to bound
- * @return the next value
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final double internalNextDouble(double origin, double bound) {
+ double r = (nextLong() >>> 11) * DOUBLE_UNIT;
+ if (origin < bound) {
+ r = r * (bound - origin) + origin;
+ if (r >= bound) // correct for rounding
+ r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
+ }
+ return r;
+ }
+
+ /**
+ * Returns a pseudorandom {@code int} value.
+ *
+ * @return a pseudorandom {@code int} value
*/
- public int nextInt(int least, int bound) {
- if (least >= bound)
- throw new IllegalArgumentException();
- return nextInt(bound - least) + least;
+ public int nextInt() {
+ return mix32(nextSeed());
+ }
+
+ /**
+ * Returns a pseudorandom {@code int} value between zero (inclusive)
+ * and the specified bound (exclusive).
+ *
+ * @param bound the upper bound (exclusive). Must be positive.
+ * @return a pseudorandom {@code int} value between zero
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code bound} is not positive
+ */
+ public int nextInt(int bound) {
+ if (bound <= 0)
+ throw new IllegalArgumentException(BadBound);
+ int r = mix32(nextSeed());
+ int m = bound - 1;
+ if ((bound & m) == 0) // power of two
+ r &= m;
+ else { // reject over-represented candidates
+ for (int u = r >>> 1;
+ u + m - (r = u % bound) < 0;
+ u = mix32(nextSeed()) >>> 1)
+ ;
+ }
+ return r;
}
/**
- * Returns a pseudorandom, uniformly distributed value
- * between 0 (inclusive) and the specified value (exclusive).
+ * Returns a pseudorandom {@code int} value between the specified
+ * origin (inclusive) and the specified bound (exclusive).
*
- * @param n the bound on the random number to be returned. Must be
- * positive.
- * @return the next value
- * @throws IllegalArgumentException if n is not positive
+ * @param origin the least value returned
+ * @param bound the upper bound (exclusive)
+ * @return a pseudorandom {@code int} value between the origin
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code origin} is greater than
+ * or equal to {@code bound}
*/
- public long nextLong(long n) {
- if (n <= 0)
- throw new IllegalArgumentException("n must be positive");
- // Divide n by two until small enough for nextInt. On each
- // iteration (at most 31 of them but usually much less),
- // randomly choose both whether to include high bit in result
- // (offset) and whether to continue with the lower vs upper
- // half (which makes a difference only if odd).
- long offset = 0;
- while (n >= Integer.MAX_VALUE) {
- int bits = next(2);
- long half = n >>> 1;
- long nextn = ((bits & 2) == 0) ? half : n - half;
- if ((bits & 1) == 0)
- offset += n - nextn;
- n = nextn;
- }
- return offset + nextInt((int) n);
+ public int nextInt(int origin, int bound) {
+ if (origin >= bound)
+ throw new IllegalArgumentException(BadRange);
+ return internalNextInt(origin, bound);
}
- @Override
- public IntStream ints() {
- return IntStream.generate(() -> current().nextInt());
+ /**
+ * Returns a pseudorandom {@code long} value.
+ *
+ * @return a pseudorandom {@code long} value
+ */
+ public long nextLong() {
+ return mix64(nextSeed());
}
- @Override
- public LongStream longs() {
- return LongStream.generate(() -> current().nextLong());
- }
-
- @Override
- public DoubleStream doubles() {
- return DoubleStream.generate(() -> current().nextDouble());
- }
-
- @Override
- public DoubleStream gaussians() {
- return DoubleStream.generate(() -> current().nextGaussian());
+ /**
+ * Returns a pseudorandom {@code long} value between zero (inclusive)
+ * and the specified bound (exclusive).
+ *
+ * @param bound the upper bound (exclusive). Must be positive.
+ * @return a pseudorandom {@code long} value between zero
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code bound} is not positive
+ */
+ public long nextLong(long bound) {
+ if (bound <= 0)
+ throw new IllegalArgumentException(BadBound);
+ long r = mix64(nextSeed());
+ long m = bound - 1;
+ if ((bound & m) == 0L) // power of two
+ r &= m;
+ else { // reject over-represented candidates
+ for (long u = r >>> 1;
+ u + m - (r = u % bound) < 0L;
+ u = mix64(nextSeed()) >>> 1)
+ ;
+ }
+ return r;
}
/**
- * Returns a pseudorandom, uniformly distributed value between the
- * given least value (inclusive) and bound (exclusive).
+ * Returns a pseudorandom {@code long} value between the specified
+ * origin (inclusive) and the specified bound (exclusive).
*
- * @param least the least value returned
+ * @param origin the least value returned
* @param bound the upper bound (exclusive)
- * @return the next value
- * @throws IllegalArgumentException if least greater than or equal
- * to bound
+ * @return a pseudorandom {@code long} value between the origin
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code origin} is greater than
+ * or equal to {@code bound}
*/
- public long nextLong(long least, long bound) {
- if (least >= bound)
- throw new IllegalArgumentException();
- return nextLong(bound - least) + least;
+ public long nextLong(long origin, long bound) {
+ if (origin >= bound)
+ throw new IllegalArgumentException(BadRange);
+ return internalNextLong(origin, bound);
+ }
+
+ /**
+ * Returns a pseudorandom {@code double} value between zero
+ * (inclusive) and one (exclusive).
+ *
+ * @return a pseudorandom {@code double} value between zero
+ * (inclusive) and one (exclusive)
+ */
+ public double nextDouble() {
+ return (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT;
}
/**
- * Returns a pseudorandom, uniformly distributed {@code double} value
- * between 0 (inclusive) and the specified value (exclusive).
+ * Returns a pseudorandom {@code double} value between 0.0
+ * (inclusive) and the specified bound (exclusive).
*
- * @param n the bound on the random number to be returned. Must be
- * positive.
- * @return the next value
- * @throws IllegalArgumentException if n is not positive
+ * @param bound the upper bound (exclusive). Must be positive.
+ * @return a pseudorandom {@code double} value between zero
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code bound} is not positive
*/
- public double nextDouble(double n) {
- if (n <= 0)
- throw new IllegalArgumentException("n must be positive");
- return nextDouble() * n;
+ public double nextDouble(double bound) {
+ if (!(bound > 0.0))
+ throw new IllegalArgumentException(BadBound);
+ double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound;
+ return (result < bound) ? result : // correct for rounding
+ Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
}
/**
- * Returns a pseudorandom, uniformly distributed value between the
- * given least value (inclusive) and bound (exclusive).
+ * Returns a pseudorandom {@code double} value between the specified
+ * origin (inclusive) and bound (exclusive).
*
- * @param least the least value returned
+ * @param origin the least value returned
* @param bound the upper bound (exclusive)
- * @return the next value
- * @throws IllegalArgumentException if least greater than or equal
- * to bound
+ * @return a pseudorandom {@code double} value between the origin
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code origin} is greater than
+ * or equal to {@code bound}
*/
- public double nextDouble(double least, double bound) {
- if (least >= bound)
- throw new IllegalArgumentException();
- return nextDouble() * (bound - least) + least;
+ public double nextDouble(double origin, double bound) {
+ if (!(origin < bound))
+ throw new IllegalArgumentException(BadRange);
+ return internalNextDouble(origin, bound);
+ }
+
+ /**
+ * Returns a pseudorandom {@code boolean} value.
+ *
+ * @return a pseudorandom {@code boolean} value
+ */
+ public boolean nextBoolean() {
+ return mix32(nextSeed()) < 0;
+ }
+
+ /**
+ * Returns a pseudorandom {@code float} value between zero
+ * (inclusive) and one (exclusive).
+ *
+ * @return a pseudorandom {@code float} value between zero
+ * (inclusive) and one (exclusive)
+ */
+ public float nextFloat() {
+ return (mix32(nextSeed()) >>> 8) * FLOAT_UNIT;
}
public double nextGaussian() {
@@ -329,6 +498,445 @@
return v1 * multiplier;
}
+ // stream methods, coded in a way intended to better isolate for
+ // maintenance purposes the small differences across forms.
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code int} values.
+ *
+ * @param streamSize the number of values to generate
+ * @return a stream of pseudorandom {@code int} values
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ * @since 1.8
+ */
+ public IntStream ints(long streamSize) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (0L, streamSize, Integer.MAX_VALUE, 0),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code int}
+ * values.
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * ints(Long.MAX_VALUE)}.
+ *
+ * @return a stream of pseudorandom {@code int} values
+ * @since 1.8
+ */
+ public IntStream ints() {
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number
+ * of pseudorandom {@code int} values, each conforming to the given
+ * origin (inclusive) and bound (exclusive).
+ *
+ * @param streamSize the number of values to generate
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code int} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero, or {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public IntStream ints(long streamSize, int randomNumberOrigin,
+ int randomNumberBound) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (0L, streamSize, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * int} values, each conforming to the given origin (inclusive) and bound
+ * (exclusive).
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
+ *
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code int} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public IntStream ints(int randomNumberOrigin, int randomNumberBound) {
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code long} values.
+ *
+ * @param streamSize the number of values to generate
+ * @return a stream of pseudorandom {@code long} values
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ * @since 1.8
+ */
+ public LongStream longs(long streamSize) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (0L, streamSize, Long.MAX_VALUE, 0L),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code long}
+ * values.
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * longs(Long.MAX_VALUE)}.
+ *
+ * @return a stream of pseudorandom {@code long} values
+ * @since 1.8
+ */
+ public LongStream longs() {
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code long}, each conforming to the given origin
+ * (inclusive) and bound (exclusive).
+ *
+ * @param streamSize the number of values to generate
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code long} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero, or {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public LongStream longs(long streamSize, long randomNumberOrigin,
+ long randomNumberBound) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (0L, streamSize, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * long} values, each conforming to the given origin (inclusive) and bound
+ * (exclusive).
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * longs(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
+ *
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code long} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public LongStream longs(long randomNumberOrigin, long randomNumberBound) {
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code double} values, each between zero
+ * (inclusive) and one (exclusive).
+ *
+ * @param streamSize the number of values to generate
+ * @return a stream of {@code double} values
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ * @since 1.8
+ */
+ public DoubleStream doubles(long streamSize) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (0L, streamSize, Double.MAX_VALUE, 0.0),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * double} values, each between zero (inclusive) and one
+ * (exclusive).
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * doubles(Long.MAX_VALUE)}.
+ *
+ * @return a stream of pseudorandom {@code double} values
+ * @since 1.8
+ */
+ public DoubleStream doubles() {
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code double} values, each conforming to the given origin
+ * (inclusive) and bound (exclusive).
+ *
+ * @param streamSize the number of values to generate
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code double} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public DoubleStream doubles(long streamSize, double randomNumberOrigin,
+ double randomNumberBound) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BadSize);
+ if (!(randomNumberOrigin < randomNumberBound))
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (0L, streamSize, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * double} values, each conforming to the given origin (inclusive) and bound
+ * (exclusive).
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * doubles(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
+ *
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code double} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ * @since 1.8
+ */
+ public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) {
+ if (!(randomNumberOrigin < randomNumberBound))
+ throw new IllegalArgumentException(BadRange);
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Spliterator for int streams. We multiplex the four int
+ * versions into one class by treating a bound less than origin as
+ * unbounded, and also by treating "infinite" as equivalent to
+ * Long.MAX_VALUE. For splits, it uses the standard divide-by-two
+ * approach. The long and double versions of this class are
+ * identical except for types.
+ */
+ static final class RandomIntsSpliterator implements Spliterator.OfInt {
+ long index;
+ final long fence;
+ final int origin;
+ final int bound;
+ RandomIntsSpliterator(long index, long fence,
+ int origin, int bound) {
+ this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomIntsSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomIntsSpliterator(i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(IntConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(ThreadLocalRandom.current().internalNextInt(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(IntConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ int o = origin, b = bound;
+ ThreadLocalRandom rng = ThreadLocalRandom.current();
+ do {
+ consumer.accept(rng.internalNextInt(o, b));
+ } while (++i < f);
+ }
+ }
+ }
+
+ /**
+ * Spliterator for long streams.
+ */
+ static final class RandomLongsSpliterator implements Spliterator.OfLong {
+ long index;
+ final long fence;
+ final long origin;
+ final long bound;
+ RandomLongsSpliterator(long index, long fence,
+ long origin, long bound) {
+ this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomLongsSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomLongsSpliterator(i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(LongConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(ThreadLocalRandom.current().internalNextLong(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(LongConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ long o = origin, b = bound;
+ ThreadLocalRandom rng = ThreadLocalRandom.current();
+ do {
+ consumer.accept(rng.internalNextLong(o, b));
+ } while (++i < f);
+ }
+ }
+
+ }
+
+ /**
+ * Spliterator for double streams.
+ */
+ static final class RandomDoublesSpliterator implements Spliterator.OfDouble {
+ long index;
+ final long fence;
+ final double origin;
+ final double bound;
+ RandomDoublesSpliterator(long index, long fence,
+ double origin, double bound) {
+ this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomDoublesSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomDoublesSpliterator(i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(DoubleConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(ThreadLocalRandom.current().internalNextDouble(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(DoubleConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ double o = origin, b = bound;
+ ThreadLocalRandom rng = ThreadLocalRandom.current();
+ do {
+ consumer.accept(rng.internalNextDouble(o, b));
+ } while (++i < f);
+ }
+ }
+ }
+
+
// Within-package utilities
/*
@@ -401,23 +1009,26 @@
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("rnd", long.class),
- new ObjectStreamField("initialized", boolean.class)
+ new ObjectStreamField("initialized", boolean.class),
};
/**
* Saves the {@code ThreadLocalRandom} to a stream (that is, serializes it).
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
*/
- private void writeObject(java.io.ObjectOutputStream out)
+ private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
- java.io.ObjectOutputStream.PutField fields = out.putFields();
+ java.io.ObjectOutputStream.PutField fields = s.putFields();
fields.put("rnd", UNSAFE.getLong(Thread.currentThread(), SEED));
fields.put("initialized", true);
- out.writeFields();
+ s.writeFields();
}
/**
* Returns the {@link #current() current} thread's {@code ThreadLocalRandom}.
+ * @return the {@link #current() current} thread's {@code ThreadLocalRandom}
*/
private Object readResolve() {
return current();
--- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongArray.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongArray.java Fri Sep 06 12:10:30 2013 -0400
@@ -303,7 +303,7 @@
* @return the previous value
* @since 1.8
*/
- public final long getAndAccumulate(int i, int x,
+ public final long getAndAccumulate(int i, long x,
LongBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i);
long prev, next;
@@ -329,7 +329,7 @@
* @return the updated value
* @since 1.8
*/
- public final long accumulateAndGet(int i, int x,
+ public final long accumulateAndGet(int i, long x,
LongBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i);
long prev, next;
--- a/jdk/src/share/classes/java/util/logging/ConsoleHandler.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/logging/ConsoleHandler.java Fri Sep 06 12:10:30 2013 -0400
@@ -26,9 +26,6 @@
package java.util.logging;
-import java.io.*;
-import java.net.*;
-
/**
* This <tt>Handler</tt> publishes log records to <tt>System.err</tt>.
* By default the <tt>SimpleFormatter</tt> is used to generate brief summaries.
@@ -114,6 +111,7 @@
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
+ @Override
public void publish(LogRecord record) {
super.publish(record);
flush();
@@ -124,6 +122,7 @@
* to close the output stream. That is, we do <b>not</b>
* close <tt>System.err</tt>.
*/
+ @Override
public void close() {
flush();
}
--- a/jdk/src/share/classes/java/util/logging/FileHandler.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/logging/FileHandler.java Fri Sep 06 12:10:30 2013 -0400
@@ -149,7 +149,7 @@
private FileChannel lockFileChannel;
private File files[];
private static final int MAX_LOCKS = 100;
- private static java.util.HashMap<String, String> locks = new java.util.HashMap<>();
+ private static final java.util.HashMap<String, String> locks = new java.util.HashMap<>();
/**
* A metered stream is a subclass of OutputStream that
@@ -157,7 +157,7 @@
* (b) keeps track of how many bytes have been written
*/
private class MeteredStream extends OutputStream {
- OutputStream out;
+ final OutputStream out;
int written;
MeteredStream(OutputStream out, int written) {
@@ -165,25 +165,30 @@
this.written = written;
}
+ @Override
public void write(int b) throws IOException {
out.write(b);
written++;
}
+ @Override
public void write(byte buff[]) throws IOException {
out.write(buff);
written += buff.length;
}
+ @Override
public void write(byte buff[], int off, int len) throws IOException {
out.write(buff,off,len);
written += len;
}
+ @Override
public void flush() throws IOException {
out.flush();
}
+ @Override
public void close() throws IOException {
out.close();
}
@@ -607,6 +612,7 @@
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
+ @Override
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
@@ -620,6 +626,7 @@
// currently being called from untrusted code.
// So it is safe to raise privilege here.
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
rotate();
return null;
@@ -634,6 +641,7 @@
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
+ @Override
public synchronized void close() throws SecurityException {
super.close();
// Unlock any lock file.
@@ -656,6 +664,7 @@
private static class InitializationErrorManager extends ErrorManager {
Exception lastException;
+ @Override
public void error(String msg, Exception ex, int code) {
lastException = ex;
}
--- a/jdk/src/share/classes/java/util/logging/Handler.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/logging/Handler.java Fri Sep 06 12:10:30 2013 -0400
@@ -47,12 +47,20 @@
public abstract class Handler {
private static final int offValue = Level.OFF.intValue();
- private LogManager manager = LogManager.getLogManager();
- private Filter filter;
- private Formatter formatter;
- private Level logLevel = Level.ALL;
- private ErrorManager errorManager = new ErrorManager();
- private String encoding;
+ private final LogManager manager = LogManager.getLogManager();
+
+ // We're using volatile here to avoid synchronizing getters, which
+ // would prevent other threads from calling isLoggable()
+ // while publish() is executing.
+ // On the other hand, setters will be synchronized to exclude concurrent
+ // execution with more complex methods, such as StreamHandler.publish().
+ // We wouldn't want 'level' to be changed by another thread in the middle
+ // of the execution of a 'publish' call.
+ private volatile Filter filter;
+ private volatile Formatter formatter;
+ private volatile Level logLevel = Level.ALL;
+ private volatile ErrorManager errorManager = new ErrorManager();
+ private volatile String encoding;
// Package private support for security checking. When sealed
// is true, we access check updates to the class.
@@ -110,7 +118,7 @@
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
- public void setFormatter(Formatter newFormatter) throws SecurityException {
+ public synchronized void setFormatter(Formatter newFormatter) throws SecurityException {
checkPermission();
// Check for a null pointer:
newFormatter.getClass();
@@ -138,7 +146,7 @@
* @exception UnsupportedEncodingException if the named encoding is
* not supported.
*/
- public void setEncoding(String encoding)
+ public synchronized void setEncoding(String encoding)
throws SecurityException, java.io.UnsupportedEncodingException {
checkPermission();
if (encoding != null) {
@@ -174,7 +182,7 @@
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
- public void setFilter(Filter newFilter) throws SecurityException {
+ public synchronized void setFilter(Filter newFilter) throws SecurityException {
checkPermission();
filter = newFilter;
}
@@ -198,7 +206,7 @@
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
- public void setErrorManager(ErrorManager em) {
+ public synchronized void setErrorManager(ErrorManager em) {
checkPermission();
if (em == null) {
throw new NullPointerException();
@@ -264,7 +272,7 @@
* than this level will be discarded.
* @return the level of messages being logged.
*/
- public synchronized Level getLevel() {
+ public Level getLevel() {
return logLevel;
}
@@ -282,11 +290,11 @@
*
*/
public boolean isLoggable(LogRecord record) {
- int levelValue = getLevel().intValue();
+ final int levelValue = getLevel().intValue();
if (record.getLevel().intValue() < levelValue || levelValue == offValue) {
return false;
}
- Filter filter = getFilter();
+ final Filter filter = getFilter();
if (filter == null) {
return true;
}
--- a/jdk/src/share/classes/java/util/logging/Level.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/logging/Level.java Fri Sep 06 12:10:30 2013 -0400
@@ -27,6 +27,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
@@ -63,7 +64,7 @@
*/
public class Level implements java.io.Serializable {
- private static String defaultBundle = "sun.util.logging.resources.logging";
+ private static final String defaultBundle = "sun.util.logging.resources.logging";
/**
* @serial The non-localized name of the level.
@@ -81,7 +82,8 @@
private final String resourceBundleName;
// localized level name
- private String localizedLevelName;
+ private transient String localizedLevelName;
+ private transient Locale cachedLocale;
/**
* OFF is a special level that can be used to turn off logging.
@@ -209,6 +211,7 @@
this.value = value;
this.resourceBundleName = resourceBundleName;
this.localizedLevelName = resourceBundleName == null ? name : null;
+ this.cachedLocale = null;
KnownLevel.add(this);
}
@@ -250,17 +253,71 @@
return this.name;
}
- final synchronized String getLocalizedLevelName() {
+ private String computeLocalizedLevelName(Locale newLocale) {
+ ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName, newLocale);
+ final String localizedName = rb.getString(name);
+
+ final boolean isDefaultBundle = defaultBundle.equals(resourceBundleName);
+ if (!isDefaultBundle) return localizedName;
+
+ // This is a trick to determine whether the name has been translated
+ // or not. If it has not been translated, we need to use Locale.ROOT
+ // when calling toUpperCase().
+ final Locale rbLocale = rb.getLocale();
+ final Locale locale =
+ Locale.ROOT.equals(rbLocale)
+ || name.equals(localizedName.toUpperCase(Locale.ROOT))
+ ? Locale.ROOT : rbLocale;
+
+ // ALL CAPS in a resource bundle's message indicates no translation
+ // needed per Oracle translation guideline. To workaround this
+ // in Oracle JDK implementation, convert the localized level name
+ // to uppercase for compatibility reason.
+ return Locale.ROOT.equals(locale) ? name : localizedName.toUpperCase(locale);
+ }
+
+ // Avoid looking up the localizedLevelName twice if we already
+ // have it.
+ final String getCachedLocalizedLevelName() {
+
if (localizedLevelName != null) {
- return localizedLevelName;
+ if (cachedLocale != null) {
+ if (cachedLocale.equals(Locale.getDefault())) {
+ // OK: our cached value was looked up with the same
+ // locale. We can use it.
+ return localizedLevelName;
+ }
+ }
}
+ if (resourceBundleName == null) {
+ // No resource bundle: just use the name.
+ return name;
+ }
+
+ // We need to compute the localized name.
+ // Either because it's the first time, or because our cached
+ // value is for a different locale. Just return null.
+ return null;
+ }
+
+ final synchronized String getLocalizedLevelName() {
+
+ // See if we have a cached localized name
+ final String cachedLocalizedName = getCachedLocalizedLevelName();
+ if (cachedLocalizedName != null) {
+ return cachedLocalizedName;
+ }
+
+ // No cached localized name or cache invalid.
+ // Need to compute the localized name.
+ final Locale newLocale = Locale.getDefault();
try {
- ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName);
- localizedLevelName = rb.getString(name);
+ localizedLevelName = computeLocalizedLevelName(newLocale);
} catch (Exception ex) {
localizedLevelName = name;
}
+ cachedLocale = newLocale;
return localizedLevelName;
}
@@ -318,6 +375,7 @@
*
* @return the non-localized name of the Level, for example "INFO".
*/
+ @Override
public final String toString() {
return name;
}
@@ -420,6 +478,7 @@
* Compare two objects for value equality.
* @return true if and only if the two objects have the same level value.
*/
+ @Override
public boolean equals(Object ox) {
try {
Level lx = (Level)ox;
@@ -433,6 +492,7 @@
* Generate a hashcode.
* @return a hashcode based on the level value
*/
+ @Override
public int hashCode() {
return this.value;
}
--- a/jdk/src/share/classes/java/util/logging/LogManager.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/logging/LogManager.java Fri Sep 06 12:10:30 2013 -0400
@@ -391,6 +391,9 @@
}
}
+ // LoggerContext maps from AppContext
+ private static WeakHashMap<Object, LoggerContext> contextsMap = null;
+
// Returns the LoggerContext for the user code (i.e. application or AppContext).
// Loggers are isolated from each AppContext.
private LoggerContext getUserContext() {
@@ -399,33 +402,28 @@
SecurityManager sm = System.getSecurityManager();
JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
if (sm != null && javaAwtAccess != null) {
+ // for each applet, it has its own LoggerContext isolated from others
synchronized (javaAwtAccess) {
- // AppContext.getAppContext() returns the system AppContext if called
- // from a system thread but Logger.getLogger might be called from
- // an applet code. Instead, find the AppContext of the applet code
- // from the execution stack.
- Object ecx = javaAwtAccess.getExecutionContext();
- if (ecx == null) {
- // fall back to thread group seach of AppContext
- ecx = javaAwtAccess.getContext();
- }
+ // find the AppContext of the applet code
+ // will be null if we are in the main app context.
+ final Object ecx = javaAwtAccess.getAppletContext();
if (ecx != null) {
- context = (LoggerContext)javaAwtAccess.get(ecx, LoggerContext.class);
+ if (contextsMap == null) {
+ contextsMap = new WeakHashMap<>();
+ }
+ context = contextsMap.get(ecx);
if (context == null) {
- if (javaAwtAccess.isMainAppContext()) {
- context = userContext;
- } else {
- // Create a new LoggerContext for the applet.
- // The new logger context has its requiresDefaultLoggers
- // flag set to true - so that these loggers will be
- // lazily added when the context is firt accessed.
- context = new LoggerContext(true);
- }
- javaAwtAccess.put(ecx, LoggerContext.class, context);
+ // Create a new LoggerContext for the applet.
+ // The new logger context has its requiresDefaultLoggers
+ // flag set to true - so that these loggers will be
+ // lazily added when the context is firt accessed.
+ context = new LoggerContext(true);
+ contextsMap.put(ecx, context);
}
}
}
}
+ // for standalone app, return userContext
return context != null ? context : userContext;
}
--- a/jdk/src/share/classes/java/util/logging/MemoryHandler.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/logging/MemoryHandler.java Fri Sep 06 12:10:30 2013 -0400
@@ -88,7 +88,7 @@
public class MemoryHandler extends Handler {
private final static int DEFAULT_SIZE = 1000;
- private Level pushLevel;
+ private volatile Level pushLevel;
private int size;
private Handler target;
private LogRecord buffer[];
@@ -188,6 +188,7 @@
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
+ @Override
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
@@ -227,6 +228,7 @@
* Note that the current contents of the <tt>MemoryHandler</tt>
* buffer are <b>not</b> written out. That requires a "push".
*/
+ @Override
public void flush() {
target.flush();
}
@@ -238,6 +240,7 @@
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
+ @Override
public void close() throws SecurityException {
target.close();
setLevel(Level.OFF);
@@ -252,11 +255,10 @@
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
- public void setPushLevel(Level newLevel) throws SecurityException {
+ public synchronized void setPushLevel(Level newLevel) throws SecurityException {
if (newLevel == null) {
throw new NullPointerException();
}
- LogManager manager = LogManager.getLogManager();
checkPermission();
pushLevel = newLevel;
}
@@ -266,7 +268,7 @@
*
* @return the value of the <tt>pushLevel</tt>
*/
- public synchronized Level getPushLevel() {
+ public Level getPushLevel() {
return pushLevel;
}
@@ -283,6 +285,7 @@
* @return true if the <tt>LogRecord</tt> would be logged.
*
*/
+ @Override
public boolean isLoggable(LogRecord record) {
return super.isLoggable(record);
}
--- a/jdk/src/share/classes/java/util/logging/SocketHandler.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/logging/SocketHandler.java Fri Sep 06 12:10:30 2013 -0400
@@ -82,7 +82,6 @@
private Socket sock;
private String host;
private int port;
- private String portProperty;
// Private method to configure a SocketHandler from LogManager
// properties and/or default values as specified in the class
@@ -177,6 +176,7 @@
* @exception SecurityException if a security manager exists and if
* the caller does not have <tt>LoggingPermission("control")</tt>.
*/
+ @Override
public synchronized void close() throws SecurityException {
super.close();
if (sock != null) {
@@ -195,6 +195,7 @@
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
+ @Override
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
--- a/jdk/src/share/classes/java/util/logging/StreamHandler.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/logging/StreamHandler.java Fri Sep 06 12:10:30 2013 -0400
@@ -73,10 +73,9 @@
*/
public class StreamHandler extends Handler {
- private LogManager manager = LogManager.getLogManager();
private OutputStream output;
private boolean doneHeader;
- private Writer writer;
+ private volatile Writer writer;
// Private method to configure a StreamHandler from LogManager
// properties and/or default values as specified in the class
@@ -169,7 +168,8 @@
* @exception UnsupportedEncodingException if the named encoding is
* not supported.
*/
- public void setEncoding(String encoding)
+ @Override
+ public synchronized void setEncoding(String encoding)
throws SecurityException, java.io.UnsupportedEncodingException {
super.setEncoding(encoding);
if (output == null) {
@@ -201,6 +201,7 @@
* @param record description of the log event. A null record is
* silently ignored and is not published
*/
+ @Override
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
@@ -240,6 +241,7 @@
* @return true if the <tt>LogRecord</tt> would be logged.
*
*/
+ @Override
public boolean isLoggable(LogRecord record) {
if (writer == null || record == null) {
return false;
@@ -250,6 +252,7 @@
/**
* Flush any buffered messages.
*/
+ @Override
public synchronized void flush() {
if (writer != null) {
try {
@@ -294,6 +297,7 @@
* @exception SecurityException if a security manager exists and if
* the caller does not have LoggingPermission("control").
*/
+ @Override
public synchronized void close() throws SecurityException {
flushAndClose();
}
--- a/jdk/src/share/classes/java/util/stream/AbstractPipeline.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/AbstractPipeline.java Fri Sep 06 12:10:30 2013 -0400
@@ -71,6 +71,9 @@
*/
abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
+ private static final String MSG_STREAM_LINKED = "stream has already been operated upon or closed";
+ private static final String MSG_CONSUMED = "source already consumed or closed";
+
/**
* Backlink to the head of the pipeline chain (self if this is the source
* stage).
@@ -137,6 +140,8 @@
*/
private boolean sourceAnyStateful;
+ private Runnable sourceCloseAction;
+
/**
* True if pipeline is parallel, otherwise the pipeline is sequential; only
* valid for the source stage.
@@ -195,7 +200,7 @@
*/
AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) {
if (previousStage.linkedOrConsumed)
- throw new IllegalStateException("stream has already been operated upon");
+ throw new IllegalStateException(MSG_STREAM_LINKED);
previousStage.linkedOrConsumed = true;
previousStage.nextStage = this;
@@ -221,7 +226,7 @@
final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
assert getOutputShape() == terminalOp.inputShape();
if (linkedOrConsumed)
- throw new IllegalStateException("stream has already been operated upon");
+ throw new IllegalStateException(MSG_STREAM_LINKED);
linkedOrConsumed = true;
return isParallel()
@@ -238,7 +243,7 @@
@SuppressWarnings("unchecked")
final Node<E_OUT> evaluateToArrayNode(IntFunction<E_OUT[]> generator) {
if (linkedOrConsumed)
- throw new IllegalStateException("stream has already been operated upon");
+ throw new IllegalStateException(MSG_STREAM_LINKED);
linkedOrConsumed = true;
// If the last intermediate operation is stateful then
@@ -266,7 +271,7 @@
throw new IllegalStateException();
if (linkedOrConsumed)
- throw new IllegalStateException("stream has already been operated upon");
+ throw new IllegalStateException(MSG_STREAM_LINKED);
linkedOrConsumed = true;
if (sourceStage.sourceSpliterator != null) {
@@ -282,7 +287,7 @@
return s;
}
else {
- throw new IllegalStateException("source already consumed");
+ throw new IllegalStateException(MSG_CONSUMED);
}
}
@@ -302,12 +307,35 @@
return (S) this;
}
+ @Override
+ public void close() {
+ linkedOrConsumed = true;
+ sourceSupplier = null;
+ sourceSpliterator = null;
+ if (sourceStage.sourceCloseAction != null) {
+ Runnable closeAction = sourceStage.sourceCloseAction;
+ sourceStage.sourceCloseAction = null;
+ closeAction.run();
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public S onClose(Runnable closeHandler) {
+ Runnable existingHandler = sourceStage.sourceCloseAction;
+ sourceStage.sourceCloseAction =
+ (existingHandler == null)
+ ? closeHandler
+ : Streams.composeWithExceptions(existingHandler, closeHandler);
+ return (S) this;
+ }
+
// Primitive specialization use co-variant overrides, hence is not final
@Override
@SuppressWarnings("unchecked")
public Spliterator<E_OUT> spliterator() {
if (linkedOrConsumed)
- throw new IllegalStateException("stream has already been operated upon");
+ throw new IllegalStateException(MSG_STREAM_LINKED);
linkedOrConsumed = true;
if (this == sourceStage) {
@@ -324,7 +352,7 @@
return lazySpliterator(s);
}
else {
- throw new IllegalStateException("source already consumed");
+ throw new IllegalStateException(MSG_CONSUMED);
}
}
else {
@@ -424,7 +452,7 @@
sourceStage.sourceSupplier = null;
}
else {
- throw new IllegalStateException("source already consumed");
+ throw new IllegalStateException(MSG_CONSUMED);
}
if (isParallel()) {
--- a/jdk/src/share/classes/java/util/stream/BaseStream.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/BaseStream.java Fri Sep 06 12:10:30 2013 -0400
@@ -35,7 +35,8 @@
* @param <S> type of stream implementing {@code BaseStream}
* @since 1.8
*/
-public interface BaseStream<T, S extends BaseStream<T, S>> {
+public interface BaseStream<T, S extends BaseStream<T, S>>
+ extends AutoCloseable {
/**
* Returns an iterator for the elements of this stream.
*
@@ -103,4 +104,33 @@
* @return an unordered stream
*/
S unordered();
+
+ /**
+ * Returns an equivalent stream with an additional close handler. Close
+ * handlers are run when the {@link #close()} method
+ * is called on the stream, and are executed in the order they were
+ * added. All close handlers are run, even if earlier close handlers throw
+ * exceptions. If any close handler throws an exception, the first
+ * exception thrown will be relayed to the caller of {@code close()}, with
+ * any remaining exceptions added to that exception as suppressed exceptions
+ * (unless one of the remaining exceptions is the same exception as the
+ * first exception, since an exception cannot suppress itself.) May
+ * return itself.
+ *
+ * <p>This is an <a href="package-summary.html#StreamOps">intermediate
+ * operation</a>.
+ *
+ * @param closeHandler A task to execute when the stream is closed
+ * @return a stream with a handler that is run if the stream is closed
+ */
+ S onClose(Runnable closeHandler);
+
+ /**
+ * Closes this stream, causing all close handlers for this stream pipeline
+ * to be called.
+ *
+ * @see AutoCloseable#close()
+ */
+ @Override
+ void close();
}
--- a/jdk/src/share/classes/java/util/stream/CloseableStream.java Fri Sep 06 12:04:18 2013 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +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. 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.util.stream;
-
-/**
- * A {@code CloseableStream} is a {@code Stream} that can be closed.
- * The close method is invoked to release resources that the object is
- * holding (such as open files).
- *
- * @param <T> The type of stream elements
- * @since 1.8
- */
-public interface CloseableStream<T> extends Stream<T>, AutoCloseable {
-
- /**
- * Closes this resource, relinquishing any underlying resources.
- * This method is invoked automatically on objects managed by the
- * {@code try}-with-resources statement. Does nothing if called when
- * the resource has already been closed.
- *
- * This method does not allow throwing checked {@code Exception}s like
- * {@link AutoCloseable#close() AutoCloseable.close()}. Cases where the
- * close operation may fail require careful attention by implementers. It
- * is strongly advised to relinquish the underlying resources and to
- * internally <em>mark</em> the resource as closed. The {@code close}
- * method is unlikely to be invoked more than once and so this ensures
- * that the resources are released in a timely manner. Furthermore it
- * reduces problems that could arise when the resource wraps, or is
- * wrapped, by another resource.
- *
- * @see AutoCloseable#close()
- */
- void close();
-}
--- a/jdk/src/share/classes/java/util/stream/DelegatingStream.java Fri Sep 06 12:04:18 2013 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,270 +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. 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.util.stream;
-
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Spliterator;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.BinaryOperator;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.IntFunction;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.function.ToDoubleFunction;
-import java.util.function.ToIntFunction;
-import java.util.function.ToLongFunction;
-
-/**
- * A {@code Stream} implementation that delegates operations to another {@code
- * Stream}.
- *
- * @param <T> type of stream elements for this stream and underlying delegate
- * stream
- *
- * @since 1.8
- */
-public class DelegatingStream<T> implements Stream<T> {
- final private Stream<T> delegate;
-
- /**
- * Construct a {@code Stream} that delegates operations to another {@code
- * Stream}.
- *
- * @param delegate the underlying {@link Stream} to which we delegate all
- * {@code Stream} methods
- * @throws NullPointerException if the delegate is null
- */
- public DelegatingStream(Stream<T> delegate) {
- this.delegate = Objects.requireNonNull(delegate);
- }
-
- // -- BaseStream methods --
-
- @Override
- public Spliterator<T> spliterator() {
- return delegate.spliterator();
- }
-
- @Override
- public boolean isParallel() {
- return delegate.isParallel();
- }
-
- @Override
- public Iterator<T> iterator() {
- return delegate.iterator();
- }
-
- // -- Stream methods --
-
- @Override
- public Stream<T> filter(Predicate<? super T> predicate) {
- return delegate.filter(predicate);
- }
-
- @Override
- public <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
- return delegate.map(mapper);
- }
-
- @Override
- public IntStream mapToInt(ToIntFunction<? super T> mapper) {
- return delegate.mapToInt(mapper);
- }
-
- @Override
- public LongStream mapToLong(ToLongFunction<? super T> mapper) {
- return delegate.mapToLong(mapper);
- }
-
- @Override
- public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
- return delegate.mapToDouble(mapper);
- }
-
- @Override
- public <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {
- return delegate.flatMap(mapper);
- }
-
- @Override
- public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
- return delegate.flatMapToInt(mapper);
- }
-
- @Override
- public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
- return delegate.flatMapToLong(mapper);
- }
-
- @Override
- public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
- return delegate.flatMapToDouble(mapper);
- }
-
- @Override
- public Stream<T> distinct() {
- return delegate.distinct();
- }
-
- @Override
- public Stream<T> sorted() {
- return delegate.sorted();
- }
-
- @Override
- public Stream<T> sorted(Comparator<? super T> comparator) {
- return delegate.sorted(comparator);
- }
-
- @Override
- public void forEach(Consumer<? super T> action) {
- delegate.forEach(action);
- }
-
- @Override
- public void forEachOrdered(Consumer<? super T> action) {
- delegate.forEachOrdered(action);
- }
-
- @Override
- public Stream<T> peek(Consumer<? super T> consumer) {
- return delegate.peek(consumer);
- }
-
- @Override
- public Stream<T> limit(long maxSize) {
- return delegate.limit(maxSize);
- }
-
- @Override
- public Stream<T> substream(long startingOffset) {
- return delegate.substream(startingOffset);
- }
-
- @Override
- public Stream<T> substream(long startingOffset, long endingOffset) {
- return delegate.substream(startingOffset, endingOffset);
- }
-
- @Override
- public <A> A[] toArray(IntFunction<A[]> generator) {
- return delegate.toArray(generator);
- }
-
- @Override
- public Object[] toArray() {
- return delegate.toArray();
- }
-
- @Override
- public T reduce(T identity, BinaryOperator<T> accumulator) {
- return delegate.reduce(identity, accumulator);
- }
-
- @Override
- public Optional<T> reduce(BinaryOperator<T> accumulator) {
- return delegate.reduce(accumulator);
- }
-
- @Override
- public <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator,
- BinaryOperator<U> combiner) {
- return delegate.reduce(identity, accumulator, combiner);
- }
-
- @Override
- public <R> R collect(Supplier<R> resultFactory,
- BiConsumer<R, ? super T> accumulator,
- BiConsumer<R, R> combiner) {
- return delegate.collect(resultFactory, accumulator, combiner);
- }
-
- @Override
- public <R, A> R collect(Collector<? super T, A, ? extends R> collector) {
- return delegate.collect(collector);
- }
-
- @Override
- public Optional<T> max(Comparator<? super T> comparator) {
- return delegate.max(comparator);
- }
-
- @Override
- public Optional<T> min(Comparator<? super T> comparator) {
- return delegate.min(comparator);
- }
-
- @Override
- public long count() {
- return delegate.count();
- }
-
- @Override
- public boolean anyMatch(Predicate<? super T> predicate) {
- return delegate.anyMatch(predicate);
- }
-
- @Override
- public boolean allMatch(Predicate<? super T> predicate) {
- return delegate.allMatch(predicate);
- }
-
- @Override
- public boolean noneMatch(Predicate<? super T> predicate) {
- return delegate.noneMatch(predicate);
- }
-
- @Override
- public Optional<T> findFirst() {
- return delegate.findFirst();
- }
-
- @Override
- public Optional<T> findAny() {
- return delegate.findAny();
- }
-
- @Override
- public Stream<T> unordered() {
- return delegate.unordered();
- }
-
- @Override
- public Stream<T> sequential() {
- return delegate.sequential();
- }
-
- @Override
- public Stream<T> parallel() {
- return delegate.parallel();
- }
-}
--- a/jdk/src/share/classes/java/util/stream/DoublePipeline.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/DoublePipeline.java Fri Sep 06 12:10:30 2013 -0400
@@ -266,10 +266,11 @@
@Override
public void accept(double t) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- DoubleStream result = mapper.apply(t);
- if (result != null)
- result.sequential().forEach(i -> downstream.accept(i));
+ try (DoubleStream result = mapper.apply(t)) {
+ // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
+ if (result != null)
+ result.sequential().forEach(i -> downstream.accept(i));
+ }
}
};
}
--- a/jdk/src/share/classes/java/util/stream/DoubleStream.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/DoubleStream.java Fri Sep 06 12:10:30 2013 -0400
@@ -752,7 +752,8 @@
* elements of a first {@code DoubleStream} succeeded by all the elements of the
* second {@code DoubleStream}. The resulting stream is ordered if both
* of the input streams are ordered, and parallel if either of the input
- * streams is parallel.
+ * streams is parallel. When the resulting stream is closed, the close
+ * handlers for both input streams are invoked.
*
* @param a the first stream
* @param b the second stream to concatenate on to end of the first stream
@@ -764,7 +765,8 @@
Spliterator.OfDouble split = new Streams.ConcatSpliterator.OfDouble(
a.spliterator(), b.spliterator());
- return StreamSupport.doubleStream(split, a.isParallel() || b.isParallel());
+ DoubleStream stream = StreamSupport.doubleStream(split, a.isParallel() || b.isParallel());
+ return stream.onClose(Streams.composedClose(a, b));
}
/**
--- a/jdk/src/share/classes/java/util/stream/IntPipeline.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/IntPipeline.java Fri Sep 06 12:10:30 2013 -0400
@@ -302,10 +302,11 @@
@Override
public void accept(int t) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- IntStream result = mapper.apply(t);
- if (result != null)
- result.sequential().forEach(i -> downstream.accept(i));
+ try (IntStream result = mapper.apply(t)) {
+ // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
+ if (result != null)
+ result.sequential().forEach(i -> downstream.accept(i));
+ }
}
};
}
--- a/jdk/src/share/classes/java/util/stream/IntStream.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/IntStream.java Fri Sep 06 12:10:30 2013 -0400
@@ -806,7 +806,8 @@
* elements of a first {@code IntStream} succeeded by all the elements of the
* second {@code IntStream}. The resulting stream is ordered if both
* of the input streams are ordered, and parallel if either of the input
- * streams is parallel.
+ * streams is parallel. When the resulting stream is closed, the close
+ * handlers for both input streams are invoked.
*
* @param a the first stream
* @param b the second stream to concatenate on to end of the first stream
@@ -818,7 +819,8 @@
Spliterator.OfInt split = new Streams.ConcatSpliterator.OfInt(
a.spliterator(), b.spliterator());
- return StreamSupport.intStream(split, a.isParallel() || b.isParallel());
+ IntStream stream = StreamSupport.intStream(split, a.isParallel() || b.isParallel());
+ return stream.onClose(Streams.composedClose(a, b));
}
/**
--- a/jdk/src/share/classes/java/util/stream/LongPipeline.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/LongPipeline.java Fri Sep 06 12:10:30 2013 -0400
@@ -283,10 +283,11 @@
@Override
public void accept(long t) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- LongStream result = mapper.apply(t);
- if (result != null)
- result.sequential().forEach(i -> downstream.accept(i));
+ try (LongStream result = mapper.apply(t)) {
+ // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
+ if (result != null)
+ result.sequential().forEach(i -> downstream.accept(i));
+ }
}
};
}
--- a/jdk/src/share/classes/java/util/stream/LongStream.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/LongStream.java Fri Sep 06 12:10:30 2013 -0400
@@ -812,7 +812,8 @@
* elements of a first {@code LongStream} succeeded by all the elements of the
* second {@code LongStream}. The resulting stream is ordered if both
* of the input streams are ordered, and parallel if either of the input
- * streams is parallel.
+ * streams is parallel. When the resulting stream is closed, the close
+ * handlers for both input streams are invoked.
*
* @param a the first stream
* @param b the second stream to concatenate on to end of the first stream
@@ -824,7 +825,8 @@
Spliterator.OfLong split = new Streams.ConcatSpliterator.OfLong(
a.spliterator(), b.spliterator());
- return StreamSupport.longStream(split, a.isParallel() || b.isParallel());
+ LongStream stream = StreamSupport.longStream(split, a.isParallel() || b.isParallel());
+ return stream.onClose(Streams.composedClose(a, b));
}
/**
--- a/jdk/src/share/classes/java/util/stream/ReferencePipeline.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/ReferencePipeline.java Fri Sep 06 12:10:30 2013 -0400
@@ -264,10 +264,11 @@
@Override
public void accept(P_OUT u) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- Stream<? extends R> result = mapper.apply(u);
- if (result != null)
- result.sequential().forEach(downstream);
+ try (Stream<? extends R> result = mapper.apply(u)) {
+ // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
+ if (result != null)
+ result.sequential().forEach(downstream);
+ }
}
};
}
@@ -291,10 +292,11 @@
@Override
public void accept(P_OUT u) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- IntStream result = mapper.apply(u);
- if (result != null)
- result.sequential().forEach(downstreamAsInt);
+ try (IntStream result = mapper.apply(u)) {
+ // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
+ if (result != null)
+ result.sequential().forEach(downstreamAsInt);
+ }
}
};
}
@@ -318,10 +320,11 @@
@Override
public void accept(P_OUT u) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- DoubleStream result = mapper.apply(u);
- if (result != null)
- result.sequential().forEach(downstreamAsDouble);
+ try (DoubleStream result = mapper.apply(u)) {
+ // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
+ if (result != null)
+ result.sequential().forEach(downstreamAsDouble);
+ }
}
};
}
@@ -345,10 +348,11 @@
@Override
public void accept(P_OUT u) {
- // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
- LongStream result = mapper.apply(u);
- if (result != null)
- result.sequential().forEach(downstreamAsLong);
+ try (LongStream result = mapper.apply(u)) {
+ // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
+ if (result != null)
+ result.sequential().forEach(downstreamAsLong);
+ }
}
};
}
@@ -493,7 +497,7 @@
@Override
@SuppressWarnings("unchecked")
- public final <R, A> R collect(Collector<? super P_OUT, A, ? extends R> collector) {
+ public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
A container;
if (isParallel()
&& (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
--- a/jdk/src/share/classes/java/util/stream/Stream.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/Stream.java Fri Sep 06 12:10:30 2013 -0400
@@ -657,7 +657,7 @@
* @see #collect(Supplier, BiConsumer, BiConsumer)
* @see Collectors
*/
- <R, A> R collect(Collector<? super T, A, ? extends R> collector);
+ <R, A> R collect(Collector<? super T, A, R> collector);
/**
* Returns the minimum element of this stream according to the provided
@@ -891,7 +891,8 @@
* elements of a first {@code Stream} succeeded by all the elements of the
* second {@code Stream}. The resulting stream is ordered if both
* of the input streams are ordered, and parallel if either of the input
- * streams is parallel.
+ * streams is parallel. When the resulting stream is closed, the close
+ * handlers for both input streams are invoked.
*
* @param <T> The type of stream elements
* @param a the first stream
@@ -906,7 +907,8 @@
@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
(Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
- return StreamSupport.stream(split, a.isParallel() || b.isParallel());
+ Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
+ return stream.onClose(Streams.composedClose(a, b));
}
/**
--- a/jdk/src/share/classes/java/util/stream/Streams.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/stream/Streams.java Fri Sep 06 12:10:30 2013 -0400
@@ -833,4 +833,61 @@
}
}
}
+
+ /**
+ * Given two Runnables, return a Runnable that executes both in sequence,
+ * even if the first throws an exception, and if both throw exceptions, add
+ * any exceptions thrown by the second as suppressed exceptions of the first.
+ */
+ static Runnable composeWithExceptions(Runnable a, Runnable b) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ try {
+ a.run();
+ }
+ catch (Throwable e1) {
+ try {
+ b.run();
+ }
+ catch (Throwable e2) {
+ try {
+ e1.addSuppressed(e2);
+ } catch (Throwable ignore) {}
+ }
+ throw e1;
+ }
+ b.run();
+ }
+ };
+ }
+
+ /**
+ * Given two streams, return a Runnable that
+ * executes both of their {@link BaseStream#close} methods in sequence,
+ * even if the first throws an exception, and if both throw exceptions, add
+ * any exceptions thrown by the second as suppressed exceptions of the first.
+ */
+ static Runnable composedClose(BaseStream<?, ?> a, BaseStream<?, ?> b) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ try {
+ a.close();
+ }
+ catch (Throwable e1) {
+ try {
+ b.close();
+ }
+ catch (Throwable e2) {
+ try {
+ e1.addSuppressed(e2);
+ } catch (Throwable ignore) {}
+ }
+ throw e1;
+ }
+ b.close();
+ }
+ };
+ }
}
--- a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java Fri Sep 06 12:10:30 2013 -0400
@@ -663,6 +663,9 @@
while (off + 4 <= len) {
int tag = get16(extra, off);
int sz = get16(extra, off + 2);
+ if (sz < 0 || (off + 4 + sz) > len) {
+ break;
+ }
if (tag == EXTID_EXTT || tag == EXTID_ZIP64) {
skipped += (sz + 4);
}
@@ -684,11 +687,18 @@
while (off + 4 <= len) {
int tag = get16(extra, off);
int sz = get16(extra, off + 2);
+ if (sz < 0 || (off + 4 + sz) > len) {
+ writeBytes(extra, off, len - off);
+ return;
+ }
if (tag != EXTID_EXTT && tag != EXTID_ZIP64) {
writeBytes(extra, off, sz + 4);
}
off += (sz + 4);
}
+ if (off < len) {
+ writeBytes(extra, off, len - off);
+ }
}
}
--- a/jdk/src/share/classes/javax/management/remote/rmi/RMIConnector.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/javax/management/remote/rmi/RMIConnector.java Fri Sep 06 12:10:30 2013 -0400
@@ -405,14 +405,7 @@
throw new IOException("Not connected");
}
- MBeanServerConnection rmbsc = rmbscMap.get(delegationSubject);
- if (rmbsc != null) {
- return rmbsc;
- }
-
- rmbsc = new RemoteMBeanServerConnection(delegationSubject);
- rmbscMap.put(delegationSubject, rmbsc);
- return rmbsc;
+ return getConnectionWithSubject(delegationSubject);
}
public void
@@ -1831,7 +1824,7 @@
// Initialization of transient variables.
private void initTransients() {
- rmbscMap = new WeakHashMap<Subject, MBeanServerConnection>();
+ rmbscMap = new WeakHashMap<Subject, WeakReference<MBeanServerConnection>>();
connected = false;
terminated = false;
@@ -2011,6 +2004,25 @@
private final ClassLoader loader;
}
+ private MBeanServerConnection getConnectionWithSubject(Subject delegationSubject) {
+ MBeanServerConnection conn = null;
+
+ if (delegationSubject == null) {
+ if (nullSubjectConnRef == null
+ || (conn = nullSubjectConnRef.get()) == null) {
+ conn = new RemoteMBeanServerConnection(null);
+ nullSubjectConnRef = new WeakReference(conn);
+ }
+ } else {
+ WeakReference<MBeanServerConnection> wr = rmbscMap.get(delegationSubject);
+ if (wr == null || (conn = wr.get()) == null) {
+ conn = new RemoteMBeanServerConnection(delegationSubject);
+ rmbscMap.put(delegationSubject, new WeakReference(conn));
+ }
+ }
+ return conn;
+ }
+
/*
The following section of code avoids a class loading problem
with RMI. The problem is that an RMI stub, when deserializing
@@ -2551,7 +2563,8 @@
private transient long clientNotifSeqNo = 0;
- private transient WeakHashMap<Subject, MBeanServerConnection> rmbscMap;
+ private transient WeakHashMap<Subject, WeakReference<MBeanServerConnection>> rmbscMap;
+ private transient WeakReference<MBeanServerConnection> nullSubjectConnRef = null;
private transient RMINotifClient rmiNotifClient;
// = new RMINotifClient(new Integer(0));
--- a/jdk/src/share/classes/sun/applet/AppletSecurity.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/applet/AppletSecurity.java Fri Sep 06 12:10:30 2013 -0400
@@ -314,7 +314,7 @@
// If we're about to allow access to the main EventQueue,
// and anything untrusted is on the class context stack,
// disallow access.
- super.checkAwtEventQueueAccess();
+ super.checkPermission(SecurityConstants.AWT.CHECK_AWT_EVENTQUEUE_PERMISSION);
}
} // checkAwtEventQueueAccess()
--- a/jdk/src/share/classes/sun/awt/AppContext.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/awt/AppContext.java Fri Sep 06 12:10:30 2013 -0400
@@ -838,21 +838,59 @@
public boolean isMainAppContext() {
return (numAppContexts.get() == 1 && mainAppContext != null);
}
- public Object getContext() {
- return getAppContext();
- }
- public Object getExecutionContext() {
- return getExecutionAppContext();
+
+ /**
+ * Returns the AppContext used for applet logging isolation, or null if
+ * the default global context can be used.
+ * If there's no applet, or if the caller is a stand alone application,
+ * or running in the main app context, returns null.
+ * Otherwise, returns the AppContext of the calling applet.
+ * @return null if the global default context can be used,
+ * an AppContext otherwise.
+ **/
+ public Object getAppletContext() {
+ // There's no AppContext: return null.
+ // No need to call getAppContext() if numAppContext == 0:
+ // it means that no AppContext has been created yet, and
+ // we don't want to trigger the creation of a main app
+ // context since we don't need it.
+ if (numAppContexts.get() == 0) return null;
+
+ // Get the context from the security manager
+ AppContext ecx = getExecutionAppContext();
+
+ // Not sure we really need to re-check numAppContexts here.
+ // If all applets have gone away then we could have a
+ // numAppContexts coming back to 0. So we recheck
+ // it here because we don't want to trigger the
+ // creation of a main AppContext in that case.
+ // This is probably not 100% MT-safe but should reduce
+ // the window of opportunity in which that issue could
+ // happen.
+ if (numAppContexts.get() > 0) {
+ // Defaults to thread group caching.
+ // This is probably not required as we only really need
+ // isolation in a deployed applet environment, in which
+ // case ecx will not be null when we reach here
+ // However it helps emulate the deployed environment,
+ // in tests for instance.
+ ecx = ecx != null ? ecx : getAppContext();
+ }
+
+ // getAppletContext() may be called when initializing the main
+ // app context - in which case mainAppContext will still be
+ // null. To work around this issue we simply use
+ // AppContext.threadGroup.getParent() == null instead, since
+ // mainAppContext is the only AppContext which should have
+ // the root TG as its thread group.
+ // See: JDK-8023258
+ final boolean isMainAppContext = ecx == null
+ || mainAppContext == ecx
+ || mainAppContext == null && ecx.threadGroup.getParent() == null;
+
+ return isMainAppContext ? null : ecx;
}
- public Object get(Object context, Object key) {
- return ((AppContext)context).get(key);
- }
- public void put(Object context, Object key, Object value) {
- ((AppContext)context).put(key, value);
- }
- public void remove(Object context, Object key) {
- ((AppContext)context).remove(key);
- }
+
});
}
}
--- a/jdk/src/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java Fri Sep 06 12:10:30 2013 -0400
@@ -57,6 +57,7 @@
import sun.awt.SunToolkit;
import sun.awt.datatransfer.DataTransferer;
import sun.awt.datatransfer.ToolkitThreadBlockedHandler;
+import sun.security.util.SecurityConstants;
/**
* <p>
@@ -225,7 +226,7 @@
SecurityManager sm = System.getSecurityManager();
try {
if (!dropInProcess && sm != null) {
- sm.checkSystemClipboardAccess();
+ sm.checkPermission(SecurityConstants.AWT.ACCESS_CLIPBOARD_PERMISSION);
}
} catch (Exception e) {
Thread currentThread = Thread.currentThread();
--- a/jdk/src/share/classes/sun/misc/JavaAWTAccess.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/misc/JavaAWTAccess.java Fri Sep 06 12:10:30 2013 -0400
@@ -26,14 +26,16 @@
package sun.misc;
public interface JavaAWTAccess {
- public Object getContext();
- public Object getExecutionContext();
- public Object get(Object context, Object key);
- public void put(Object context, Object key, Object value);
- public void remove(Object context, Object key);
+ // Returns the AppContext used for applet logging isolation, or null if
+ // no isolation is required.
+ // If there's no applet, or if the caller is a stand alone application,
+ // or running in the main app context, returns null.
+ // Otherwise, returns the AppContext of the calling applet.
+ public Object getAppletContext();
- // convenience methods whose context is the object returned by getContext()
+ // convenience methods to cache objects in the current thread group's
+ // AppContext
public Object get(Object key);
public void put(Object key, Object value);
public void remove(Object key);
--- a/jdk/src/share/classes/sun/misc/SharedSecrets.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/misc/SharedSecrets.java Fri Sep 06 12:10:30 2013 -0400
@@ -170,7 +170,7 @@
public static JavaAWTAccess getJavaAWTAccess() {
// this may return null in which case calling code needs to
// provision for.
- if (javaAWTAccess == null || javaAWTAccess.getContext() == null) {
+ if (javaAWTAccess == null) {
return null;
}
return javaAWTAccess;
--- a/jdk/src/share/classes/sun/nio/ch/AbstractPollSelectorImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/nio/ch/AbstractPollSelectorImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -193,9 +193,4 @@
if (!selch.isOpen() && !selch.isRegistered())
((SelChImpl)selch).kill();
}
-
- static {
- Util.load();
- }
-
}
--- a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -1138,7 +1138,7 @@
throws IOException;
static {
- Util.load();
+ IOUtil.load();
initIDs();
}
--- a/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -1162,7 +1162,7 @@
private static native long initIDs();
static {
- Util.load();
+ IOUtil.load();
allocationGranularity = initIDs();
}
--- a/jdk/src/share/classes/sun/nio/ch/IOUtil.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/nio/ch/IOUtil.java Fri Sep 06 12:10:30 2013 -0400
@@ -347,9 +347,23 @@
static native void initIDs();
+ /**
+ * Used to trigger loading of native libraries
+ */
+ public static void load() { }
+
static {
- // Note that IOUtil.initIDs is called from within Util.load.
- Util.load();
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("net");
+ System.loadLibrary("nio");
+ return null;
+ }
+ });
+
+ initIDs();
+
IOV_MAX = iovMax();
}
--- a/jdk/src/share/classes/sun/nio/ch/Net.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/nio/ch/Net.java Fri Sep 06 12:10:30 2013 -0400
@@ -582,7 +582,7 @@
private static native void initIDs();
static {
- Util.load();
+ IOUtil.load();
initIDs();
}
--- a/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -416,7 +416,7 @@
private static native void initIDs();
static {
- Util.load();
+ IOUtil.load();
initIDs();
nd = new SocketDispatcher();
}
--- a/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -1024,7 +1024,7 @@
throws IOException;
static {
- Util.load();
+ IOUtil.load();
nd = new SocketDispatcher();
}
--- a/jdk/src/share/classes/sun/nio/ch/Util.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/nio/ch/Util.java Fri Sep 06 12:10:30 2013 -0400
@@ -401,30 +401,4 @@
return bugLevel.equals(bl);
}
-
-
- // -- Initialization --
-
- private static boolean loaded = false;
-
- public static void load() {
- synchronized (Util.class) {
- if (loaded)
- return;
- loaded = true;
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("net");
- System.loadLibrary("nio");
- return null;
- }
- });
-
- // IOUtil must be initialized; Its native methods are called from
- // other places in native nio code so they must be set up.
- IOUtil.initIDs();
- }
- }
-
}
--- a/jdk/src/share/classes/sun/swing/SwingUtilities2.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/swing/SwingUtilities2.java Fri Sep 06 12:10:30 2013 -0400
@@ -1184,7 +1184,7 @@
canAccess = true;
} else {
try {
- sm.checkSystemClipboardAccess();
+ sm.checkPermission(SecurityConstants.AWT.ACCESS_CLIPBOARD_PERMISSION);
canAccess = true;
} catch (SecurityException e) {
}
--- a/jdk/src/share/classes/sun/tools/jar/resources/jar.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/tools/jar/resources/jar.properties Fri Sep 06 12:10:30 2013 -0400
@@ -61,7 +61,7 @@
out.extracted=\
extracted: {0}
out.inflated=\
- \ \inflated: {0}
+ \ inflated: {0}
out.size=\
(in = {0}) (out= {1})
--- a/jdk/src/share/classes/sun/tools/jconsole/Resources.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/tools/jconsole/Resources.java Fri Sep 06 12:10:30 2013 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -30,7 +30,7 @@
import java.lang.reflect.Modifier;
import java.text.MessageFormat;
import java.util.Collections;
-import java.util.HashMap;
+import java.util.IdentityHashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
@@ -40,7 +40,7 @@
*/
public final class Resources {
private static Map<String, Integer> MNEMONIC_LOOKUP = Collections
- .synchronizedMap(new HashMap<String, Integer>());
+ .synchronizedMap(new IdentityHashMap<String, Integer>());
private Resources() {
throw new AssertionError();
--- a/jdk/src/share/classes/sun/tracing/ProviderSkeleton.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/tracing/ProviderSkeleton.java Fri Sep 06 12:10:30 2013 -0400
@@ -164,7 +164,10 @@
declaringClass == Object.class) {
return method.invoke(this, args);
} else {
- assert false;
+ // assert false : "this should never happen"
+ // reaching here would indicate a breach
+ // in security in the higher layers
+ throw new SecurityException();
}
} catch (IllegalAccessException e) {
assert false;
--- a/jdk/src/share/classes/sun/util/logging/resources/logging.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging.properties Fri Sep 06 12:10:30 2013 -0400
@@ -27,20 +27,20 @@
# these are the same as the non-localized level name.
# The following ALL CAPS words should be translated.
-ALL=ALL
+ALL=All
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=Severe
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=Warning
# The following ALL CAPS words should be translated.
-INFO=INFO
+INFO=Info
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= Config
# The following ALL CAPS words should be translated.
-FINE=FINE
+FINE=Fine
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=Finer
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=Finest
# The following ALL CAPS words should be translated.
-OFF=OFF
+OFF=Off
--- a/jdk/src/share/classes/sun/util/logging/resources/logging_de.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging_de.properties Fri Sep 06 12:10:30 2013 -0400
@@ -27,20 +27,20 @@
# these are the same as the non-localized level name.
# The following ALL CAPS words should be translated.
-ALL=ALL
+ALL=Alle
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=Schwerwiegend
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=Warnung
# The following ALL CAPS words should be translated.
-INFO=INFO
+INFO=Information
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= Konfiguration
# The following ALL CAPS words should be translated.
-FINE=FINE
+FINE=Fein
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=Feiner
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=Am feinsten
# The following ALL CAPS words should be translated.
-OFF=OFF
+OFF=Deaktiviert
--- a/jdk/src/share/classes/sun/util/logging/resources/logging_es.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging_es.properties Fri Sep 06 12:10:30 2013 -0400
@@ -27,20 +27,20 @@
# these are the same as the non-localized level name.
# The following ALL CAPS words should be translated.
-ALL=ALL
+ALL=Todo
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=Grave
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=Advertencia
# The following ALL CAPS words should be translated.
-INFO=INFO
+INFO=Informaci\u00F3n
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= Configurar
# The following ALL CAPS words should be translated.
-FINE=FINE
+FINE=Detallado
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=Muy Detallado
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=M\u00E1s Detallado
# The following ALL CAPS words should be translated.
-OFF=OFF
+OFF=Desactivado
--- a/jdk/src/share/classes/sun/util/logging/resources/logging_fr.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging_fr.properties Fri Sep 06 12:10:30 2013 -0400
@@ -27,20 +27,20 @@
# these are the same as the non-localized level name.
# The following ALL CAPS words should be translated.
-ALL=ALL
+ALL=Tout
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=Grave
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=Avertissement
# The following ALL CAPS words should be translated.
-INFO=INFO
+INFO=Infos
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= Config
# The following ALL CAPS words should be translated.
-FINE=FINE
+FINE=Pr\u00E9cis
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=Plus pr\u00E9cis
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=Le plus pr\u00E9cis
# The following ALL CAPS words should be translated.
-OFF=OFF
+OFF=D\u00E9sactiv\u00E9
--- a/jdk/src/share/classes/sun/util/logging/resources/logging_it.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging_it.properties Fri Sep 06 12:10:30 2013 -0400
@@ -27,20 +27,20 @@
# these are the same as the non-localized level name.
# The following ALL CAPS words should be translated.
-ALL=ALL
+ALL=Tutto
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=Grave
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=Avvertenza
# The following ALL CAPS words should be translated.
-INFO=INFO
+INFO=Informazioni
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= Configurazione
# The following ALL CAPS words should be translated.
-FINE=FINE
+FINE=Buono
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=Migliore
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=Ottimale
# The following ALL CAPS words should be translated.
-OFF=OFF
+OFF=Non attivo
--- a/jdk/src/share/classes/sun/util/logging/resources/logging_ja.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging_ja.properties Fri Sep 06 12:10:30 2013 -0400
@@ -29,18 +29,18 @@
# The following ALL CAPS words should be translated.
ALL=\u3059\u3079\u3066
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=\u91CD\u5927
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=\u8B66\u544A
# The following ALL CAPS words should be translated.
INFO=\u60C5\u5831
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= \u69CB\u6210
# The following ALL CAPS words should be translated.
-FINE=\u8A73\u7D30\u30EC\u30D9\u30EB(\u4F4E)
+FINE=\u666E\u901A
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=\u8A73\u7D30
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=\u6700\u3082\u8A73\u7D30
# The following ALL CAPS words should be translated.
OFF=\u30AA\u30D5
--- a/jdk/src/share/classes/sun/util/logging/resources/logging_ko.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging_ko.properties Fri Sep 06 12:10:30 2013 -0400
@@ -27,20 +27,20 @@
# these are the same as the non-localized level name.
# The following ALL CAPS words should be translated.
-ALL=ALL
+ALL=\uBAA8\uB450
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=\uC2EC\uAC01
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=\uACBD\uACE0
# The following ALL CAPS words should be translated.
-INFO=INFO
+INFO=\uC815\uBCF4
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= \uAD6C\uC131
# The following ALL CAPS words should be translated.
-FINE=FINE
+FINE=\uBBF8\uC138
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=\uBCF4\uB2E4 \uBBF8\uC138
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=\uAC00\uC7A5 \uBBF8\uC138
# The following ALL CAPS words should be translated.
-OFF=OFF
+OFF=\uD574\uC81C
--- a/jdk/src/share/classes/sun/util/logging/resources/logging_pt_BR.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging_pt_BR.properties Fri Sep 06 12:10:30 2013 -0400
@@ -27,20 +27,20 @@
# these are the same as the non-localized level name.
# The following ALL CAPS words should be translated.
-ALL=ALL
+ALL=Tudo
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=Grave
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=Advert\u00EAncia
# The following ALL CAPS words should be translated.
-INFO=INFO
+INFO=Informa\u00E7\u00F5es
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= Configura\u00E7\u00E3o
# The following ALL CAPS words should be translated.
-FINE=FINE
+FINE=Detalhado
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=Mais Detalhado
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=O Mais Detalhado
# The following ALL CAPS words should be translated.
-OFF=OFF
+OFF=Desativado
--- a/jdk/src/share/classes/sun/util/logging/resources/logging_sv.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging_sv.properties Fri Sep 06 12:10:30 2013 -0400
@@ -27,20 +27,20 @@
# these are the same as the non-localized level name.
# The following ALL CAPS words should be translated.
-ALL=ALLA
+ALL=Alla
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=Allvarlig
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=Varning
# The following ALL CAPS words should be translated.
-INFO=INFO
+INFO=Info
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= Konfig
# The following ALL CAPS words should be translated.
-FINE=FINE
+FINE=Fin
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=Finare
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=Finaste
# The following ALL CAPS words should be translated.
-OFF=OFF
+OFF=Av
--- a/jdk/src/share/classes/sun/util/logging/resources/logging_zh_CN.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging_zh_CN.properties Fri Sep 06 12:10:30 2013 -0400
@@ -27,20 +27,20 @@
# these are the same as the non-localized level name.
# The following ALL CAPS words should be translated.
-ALL=ALL
+ALL=\u5168\u90E8
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=\u4E25\u91CD
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=\u8B66\u544A
# The following ALL CAPS words should be translated.
-INFO=INFO
+INFO=\u4FE1\u606F
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= \u914D\u7F6E
# The following ALL CAPS words should be translated.
-FINE=FINE
+FINE=\u8BE6\u7EC6
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=\u8F83\u8BE6\u7EC6
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=\u975E\u5E38\u8BE6\u7EC6
# The following ALL CAPS words should be translated.
-OFF=OFF
+OFF=\u7981\u7528
--- a/jdk/src/share/classes/sun/util/logging/resources/logging_zh_TW.properties Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/classes/sun/util/logging/resources/logging_zh_TW.properties Fri Sep 06 12:10:30 2013 -0400
@@ -27,20 +27,20 @@
# these are the same as the non-localized level name.
# The following ALL CAPS words should be translated.
-ALL=\u6240\u6709
+ALL=\u5168\u90E8
# The following ALL CAPS words should be translated.
-SEVERE=SEVERE
+SEVERE=\u56B4\u91CD
# The following ALL CAPS words should be translated.
-WARNING=WARNING
+WARNING=\u8B66\u544A
# The following ALL CAPS words should be translated.
INFO=\u8CC7\u8A0A
# The following ALL CAPS words should be translated.
-CONFIG= CONFIG
+CONFIG= \u7D44\u614B
# The following ALL CAPS words should be translated.
FINE=\u8A73\u7D30
# The following ALL CAPS words should be translated.
-FINER=FINER
+FINER=\u8F03\u8A73\u7D30
# The following ALL CAPS words should be translated.
-FINEST=FINEST
+FINEST=\u6700\u8A73\u7D30
# The following ALL CAPS words should be translated.
OFF=\u95DC\u9589
--- a/jdk/src/share/native/common/check_code.c Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/share/native/common/check_code.c Fri Sep 06 12:10:30 2013 -0400
@@ -90,6 +90,12 @@
#include "classfile_constants.h"
#include "opcodes.in_out"
+#ifdef __APPLE__
+/* use setjmp/longjmp versions that do not save/restore the signal mask */
+#define setjmp _setjmp
+#define longjmp _longjmp
+#endif
+
#define MAX_ARRAY_DIMENSIONS 255
/* align byte code */
#ifndef ALIGN_UP
--- a/jdk/src/solaris/classes/sun/awt/X11/XToolkit.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/awt/X11/XToolkit.java Fri Sep 06 12:10:30 2013 -0400
@@ -54,6 +54,7 @@
import sun.security.action.GetPropertyAction;
import sun.security.action.GetBooleanAction;
import sun.util.logging.PlatformLogger;
+import sun.security.util.SecurityConstants;
public final class XToolkit extends UNIXToolkit implements Runnable {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XToolkit");
@@ -1152,7 +1153,7 @@
public Clipboard getSystemClipboard() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
- security.checkSystemClipboardAccess();
+ security.checkPermission(SecurityConstants.AWT.ACCESS_CLIPBOARD_PERMISSION);
}
synchronized (this) {
if (clipboard == null) {
@@ -1165,7 +1166,7 @@
public Clipboard getSystemSelection() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
- security.checkSystemClipboardAccess();
+ security.checkPermission(SecurityConstants.AWT.ACCESS_CLIPBOARD_PERMISSION);
}
synchronized (this) {
if (selection == null) {
--- a/jdk/src/solaris/classes/sun/nio/ch/DatagramDispatcher.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/DatagramDispatcher.java Fri Sep 06 12:10:30 2013 -0400
@@ -36,7 +36,7 @@
class DatagramDispatcher extends NativeDispatcher
{
static {
- Util.load();
+ IOUtil.load();
}
int read(FileDescriptor fd, long address, int len) throws IOException {
--- a/jdk/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java Fri Sep 06 12:10:30 2013 -0400
@@ -316,4 +316,8 @@
private native int poll0(long pollAddress, int numfds, long timeout,
int wfd);
private static native void interrupt(int fd);
+
+ static {
+ IOUtil.load();
+ }
}
--- a/jdk/src/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -196,9 +196,4 @@
}
return this;
}
-
- static {
- Util.load();
- }
-
}
--- a/jdk/src/solaris/classes/sun/nio/ch/EPoll.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/EPoll.java Fri Sep 06 12:10:30 2013 -0400
@@ -113,6 +113,6 @@
throws IOException;
static {
- Util.load();
+ IOUtil.load();
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java Fri Sep 06 12:10:30 2013 -0400
@@ -318,6 +318,7 @@
}
static {
+ IOUtil.load();
init();
}
--- a/jdk/src/solaris/classes/sun/nio/ch/EPollPort.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/EPollPort.java Fri Sep 06 12:10:30 2013 -0400
@@ -318,6 +318,6 @@
private static native void close0(int fd);
static {
- Util.load();
+ IOUtil.load();
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -196,8 +196,4 @@
}
return this;
}
-
- static {
- Util.load();
- }
}
--- a/jdk/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -31,7 +31,7 @@
{
static {
- Util.load();
+ IOUtil.load();
init();
}
--- a/jdk/src/solaris/classes/sun/nio/ch/InheritedChannel.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/InheritedChannel.java Fri Sep 06 12:10:30 2013 -0400
@@ -235,6 +235,6 @@
private static native int peerPort0(int fd);
static {
- Util.load();
+ IOUtil.load();
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/KQueue.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/KQueue.java Fri Sep 06 12:10:30 2013 -0400
@@ -115,6 +115,6 @@
throws IOException;
static {
- Util.load();
+ IOUtil.load();
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/KQueuePort.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/KQueuePort.java Fri Sep 06 12:10:30 2013 -0400
@@ -326,6 +326,6 @@
private static native void close0(int fd);
static {
- Util.load();
+ IOUtil.load();
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/NativeThread.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/NativeThread.java Fri Sep 06 12:10:30 2013 -0400
@@ -54,7 +54,7 @@
private static native void init();
static {
- Util.load();
+ IOUtil.load();
init();
}
--- a/jdk/src/solaris/classes/sun/nio/ch/PollArrayWrapper.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/PollArrayWrapper.java Fri Sep 06 12:10:30 2013 -0400
@@ -126,4 +126,7 @@
private static native void interrupt(int fd);
+ static {
+ IOUtil.load();
+ }
}
--- a/jdk/src/solaris/classes/sun/nio/ch/SinkChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/SinkChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -37,7 +37,7 @@
{
// Used to make native read and write calls
- private static NativeDispatcher nd;
+ private static final NativeDispatcher nd = new FileDispatcherImpl();
// The file descriptor associated with this channel
FileDescriptor fd;
@@ -206,10 +206,4 @@
throw new IndexOutOfBoundsException();
return write(Util.subsequence(srcs, offset, length));
}
-
- static {
- Util.load();
- nd = new FileDispatcherImpl();
- }
-
}
--- a/jdk/src/solaris/classes/sun/nio/ch/SolarisEventPort.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/SolarisEventPort.java Fri Sep 06 12:10:30 2013 -0400
@@ -260,6 +260,6 @@
static {
- Util.load();
+ IOUtil.load();
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/SourceChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/SourceChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -37,7 +37,7 @@
{
// Used to make native read and write calls
- private static NativeDispatcher nd;
+ private static final NativeDispatcher nd = new FileDispatcherImpl();
// The file descriptor associated with this channel
FileDescriptor fd;
@@ -206,10 +206,4 @@
}
}
}
-
- static {
- Util.load();
- nd = new FileDispatcherImpl();
- }
-
}
--- a/jdk/src/solaris/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -345,7 +345,7 @@
throws IOException;
static {
- Util.load();
+ IOUtil.load();
initIDs();
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -748,6 +748,6 @@
private static native void checkConnect(int fdVal) throws IOException;
static {
- Util.load();
+ IOUtil.load();
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/sctp/SctpChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/sctp/SctpChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -1106,7 +1106,7 @@
boolean ready) throws IOException;
static {
- Util.load(); /* loads nio & net native libraries */
+ IOUtil.load(); /* loads nio & net native libraries */
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
--- a/jdk/src/solaris/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -995,7 +995,7 @@
}
static {
- Util.load(); /* loads nio & net native libraries */
+ IOUtil.load(); /* loads nio & net native libraries */
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
--- a/jdk/src/solaris/classes/sun/nio/ch/sctp/SctpServerChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/ch/sctp/SctpServerChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -426,7 +426,7 @@
FileDescriptor newfd, InetSocketAddress[] isaa) throws IOException;
static {
- Util.load(); // loads nio & net native libraries
+ IOUtil.load(); // loads nio & net native libraries
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
--- a/jdk/src/windows/classes/sun/awt/windows/WToolkit.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/awt/windows/WToolkit.java Fri Sep 06 12:10:30 2013 -0400
@@ -64,6 +64,7 @@
import sun.font.SunFontManager;
import sun.misc.PerformanceLogger;
import sun.util.logging.PlatformLogger;
+import sun.security.util.SecurityConstants;
public class WToolkit extends SunToolkit implements Runnable {
@@ -681,7 +682,7 @@
public Clipboard getSystemClipboard() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
- security.checkSystemClipboardAccess();
+ security.checkPermission(SecurityConstants.AWT.ACCESS_CLIPBOARD_PERMISSION);
}
synchronized (this) {
if (clipboard == null) {
--- a/jdk/src/windows/classes/sun/nio/ch/DatagramDispatcher.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/nio/ch/DatagramDispatcher.java Fri Sep 06 12:10:30 2013 -0400
@@ -36,7 +36,7 @@
class DatagramDispatcher extends NativeDispatcher
{
static {
- Util.load();
+ IOUtil.load();
}
int read(FileDescriptor fd, long address, int len) throws IOException {
--- a/jdk/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -32,7 +32,7 @@
class FileDispatcherImpl extends FileDispatcher
{
static {
- Util.load();
+ IOUtil.load();
}
/**
--- a/jdk/src/windows/classes/sun/nio/ch/FileKey.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/nio/ch/FileKey.java Fri Sep 06 12:10:30 2013 -0400
@@ -73,6 +73,7 @@
private static native void initIDs();
static {
+ IOUtil.load();
initIDs();
}
}
--- a/jdk/src/windows/classes/sun/nio/ch/Iocp.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/nio/ch/Iocp.java Fri Sep 06 12:10:30 2013 -0400
@@ -443,7 +443,7 @@
private static native String getErrorMessage(int error);
static {
- Util.load();
+ IOUtil.load();
initIDs();
// thread agnostic I/O on Vista/2008 or newer
--- a/jdk/src/windows/classes/sun/nio/ch/PipeImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/nio/ch/PipeImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -56,7 +56,6 @@
private static final Random rnd;
static {
- Util.load();
byte[] someBytes = new byte[8];
boolean resultOK = IOUtil.randomBytes(someBytes);
if (resultOK) {
--- a/jdk/src/windows/classes/sun/nio/ch/SocketDispatcher.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/nio/ch/SocketDispatcher.java Fri Sep 06 12:10:30 2013 -0400
@@ -36,7 +36,7 @@
{
static {
- Util.load();
+ IOUtil.load();
}
int read(FileDescriptor fd, long address, int len) throws IOException {
--- a/jdk/src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -750,6 +750,6 @@
private static native void close0(long handle);
static {
- Util.load();
+ IOUtil.load();
}
}
--- a/jdk/src/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -363,7 +363,7 @@
private static native void closesocket0(long socket) throws IOException;
static {
- Util.load();
+ IOUtil.load();
initIDs();
}
}
--- a/jdk/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -919,7 +919,7 @@
private static native void closesocket0(long socket) throws IOException;
static {
- Util.load();
+ IOUtil.load();
initIDs();
}
}
--- a/jdk/src/windows/classes/sun/nio/ch/WindowsSelectorImpl.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/src/windows/classes/sun/nio/ch/WindowsSelectorImpl.java Fri Sep 06 12:10:30 2013 -0400
@@ -611,6 +611,6 @@
}
static {
- Util.load();
+ IOUtil.load();
}
}
--- a/jdk/test/ProblemList.txt Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/ProblemList.txt Fri Sep 06 12:10:30 2013 -0400
@@ -205,13 +205,6 @@
############################################################################
-# jdk_io
-
-# 7160013
-#java/io/File/MaxPathLength.java windows-all
-
-############################################################################
-
# jdk_nio
# 6963118
@@ -312,10 +305,6 @@
# 6461635
com/sun/tools/attach/BasicTests.sh generic-all
-# 7172176
-sun/tools/jconsole/ResourceCheckTest.sh generic-all
-sun/tools/jconsole/ImmutableResourceTest.sh generic-all
-
# 7132203
sun/jvmstat/monitor/MonitoredVm/CR6672135.java generic-all
--- a/jdk/test/TEST.groups Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/TEST.groups Fri Sep 06 12:10:30 2013 -0400
@@ -27,6 +27,7 @@
sun/invoke \
sun/misc \
sun/reflect \
+ jdk/lambda \
vm
jdk_util = \
@@ -133,7 +134,6 @@
javax/xml \
-javax/xml/crypto \
jdk/asm \
- jdk/lambda \
com/sun/jndi \
com/sun/corba \
lib/testlibrary \
@@ -212,3 +212,341 @@
:jdk_swing \
:jdk_sound \
:jdk_imageio
+
+###############################################################################
+# Profile-based Test Group Definitions
+#
+# These groups define the tests that cover the different possible runtimes:
+# - compact1, compact2, compact3, full JRE, JDK
+#
+# In addition they support testing of the minimal VM on compact1 and compact2.
+# Essentially this defines groups based around the specified API's and VM
+# services available in the runtime.
+#
+# The groups are defined hierarchically in two forms:
+# - The need_xxx groups list all the tests that have a dependency on
+# a specific profile. This is either because it tests a feature in
+# that profile, or the test infrastructure uses a feature in that
+# profile.
+# - The primary groups are defined in terms of the other primary groups
+# combined with the needs_xxx groups (including and excluding them as
+# appropriate). For example the jre can run all tests from compact3, plus
+# those from needs_jre, but excluding those from need_jdk.
+#
+# The bottom group defines all the actual tests to be considered, simply
+# by listing the top-level test directories.
+
+# Full JDK can run all tests
+#
+jdk = \
+ :jre \
+ :needs_jdk
+
+# Tests that require a full JDK to execute. Either they test a feature
+# only in the JDK or they use tools that are only in the JDK. The latter
+# can be resolved in some cases by using tools from the compile-jdk.
+#
+needs_jdk = \
+ :jdk_jdi \
+ com/sun/tools \
+ demo \
+ sun/security/tools/jarsigner \
+ sun/rmi/rmic \
+ sun/tools \
+ sun/jvmstat \
+ tools \
+ com/sun/jmx/remote/NotificationMarshalVersions/TestSerializationMismatch.java \
+ java/io/Serializable/serialver \
+ java/lang/invoke/lambda/LambdaAccessControlDoPrivilegedTest.java \
+ java/lang/invoke/lambda/LambdaAccessControlTest.java \
+ java/lang/System/MacEncoding/TestFileEncoding.java \
+ java/net/URLClassLoader/closetest/GetResourceAsStream.java \
+ java/util/Collections/EmptyIterator.java \
+ java/util/concurrent/locks/Lock/TimedAcquireLeak.java \
+ java/util/jar/Manifest/CreateManifest.java \
+ java/util/jar/JarInputStream/ExtraFileInMetaInf.java \
+ java/util/logging/AnonLoggerWeakRefLeak.sh \
+ java/util/logging/LoggerWeakRefLeak.sh \
+ java/util/zip/3GBZipFiles.sh \
+ jdk/lambda/FDTest.java \
+ jdk/lambda/separate/Compiler.java \
+ sun/management/jdp/JdpTest.sh \
+ sun/management/jmxremote/bootstrap/JvmstatCountersTest.java \
+ sun/management/jmxremote/bootstrap/LocalManagementTest.sh \
+ sun/misc/JarIndex/metaInfFilenames/Basic.java \
+ sun/misc/JarIndex/JarIndexMergeForClassLoaderTest.java \
+ sun/reflect/CallerSensitive/CallerSensitiveFinder.java \
+ sun/reflect/CallerSensitive/MissingCallerSensitive.java \
+ sun/security/util/Resources/NewNamesFormat.java \
+ vm/verifier/defaultMethods/DefaultMethodRegressionTestsRun.java
+
+# JRE adds further tests to compact3
+#
+jre = \
+ :compact3 \
+ :needs_jre \
+ -:needs_jdk
+
+# Tests that require the full JRE
+#
+needs_jre = \
+ :needs_charsets \
+ :jdk_desktop \
+ com/sun/corba \
+ com/sun/jndi/cosnaming \
+ sun/net/ftp \
+ sun/net/www/protocol/ftp \
+ sun/security/tools/policytool \
+ java/net/URI/URItoURLTest.java \
+ java/net/URL/URIToURLTest.java \
+ java/net/URLConnection/HandleContentTypeWithAttrs.java \
+ java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.sh \
+ java/security/Security/ClassLoaderDeadlock/Deadlock.sh \
+ java/util/logging/Listeners.java \
+ java/util/logging/ListenersWithSM.java \
+ java/util/ResourceBundle/Control/Bug6530694.java \
+ java/text/Bidi/BidiConformance.java \
+ java/text/Bidi/BidiEmbeddingTest.java \
+ java/text/Bidi/Bug6665028.java \
+ java/text/Bidi/Bug7042148.java \
+ java/text/Bidi/Bug7051769.java \
+ javax/crypto/Cipher/CipherStreamClose.java \
+ javax/management/monitor/AttributeArbitraryDataTypeTest.java \
+ jdk/lambda/vm/InterfaceAccessFlagsTest.java \
+ sun/misc/URLClassPath/ClassnameCharTest.java
+
+# Tests dependent on the optional charsets.jar
+# These are isolated for easy exclusions
+#
+needs_charsets = \
+ java/io/OutputStreamWriter/TestWrite.java \
+ java/nio/charset/RemovingSunIO/SunioAlias.java \
+ java/nio/charset/coders/Check.java \
+ java/nio/charset/Charset/CharsetContainmentTest.java \
+ java/nio/charset/Charset/Contains.java \
+ java/nio/charset/Charset/NIOCharsetAvailabilityTest.java \
+ java/nio/charset/Charset/RegisteredCharsets.java \
+ java/nio/charset/CharsetEncoder/Flush.java \
+ java/nio/charset/coders/CheckSJISMappingProp.sh \
+ java/nio/charset/coders/ResetISO2022JP.java \
+ java/util/Locale/InternationalBAT.java \
+ java/util/Locale/LocaleProviders.sh \
+ java/util/Calendar/CldrFormatNamesTest.java \
+ java/util/TimeZone/CLDRDisplayNamesTest.java \
+ java/util/zip/ZipCoding.java \
+ sun/nio/cs/EucJpLinux0212.java \
+ sun/nio/cs/EUCJPUnderflowDecodeTest.java \
+ sun/nio/cs/EuroConverter.java \
+ sun/nio/cs/JISAutoDetectTest.java \
+ sun/nio/cs/OLD/TestIBMDB.java \
+ sun/nio/cs/SJISCanEncode.java \
+ sun/nio/cs/Test6254467.java \
+ sun/nio/cs/TestCompoundTest.java \
+ sun/nio/cs/TestCp834_SBCS.java \
+ sun/nio/cs/TestEUC_TW.java \
+ sun/nio/cs/TestISO2022CNDecoder.java \
+ sun/nio/cs/TestISO2022JPEncoder.java \
+ sun/nio/cs/TestISO2022JPSubBytes.java \
+ sun/nio/cs/TestIllegalSJIS.java \
+ sun/nio/cs/TestJIS0208Decoder.java \
+ sun/nio/cs/TestJIS0212Decoder.java \
+ sun/nio/cs/TestMiscEUC_JP.java \
+ sun/nio/cs/TestSJIS0213_SM.java \
+ sun/nio/cs/BufferUnderflowEUCTWTest.java \
+ sun/nio/cs/CheckCaseInsensitiveEncAliases.java \
+ sun/nio/cs/CheckHistoricalNames.java \
+ sun/nio/cs/EucJpLinuxDecoderRecoveryTest.java \
+ sun/nio/cs/HWKatakanaMS932EncodeTest.java \
+ sun/nio/cs/ISCIITest.java \
+ sun/nio/cs/LatinCharReplacementTWTest.java \
+ sun/nio/cs/NIOJISAutoDetectTest.java \
+ sun/nio/cs/StreamEncoderClose.java \
+ sun/nio/cs/SurrogateGB18030Test.java \
+ sun/nio/cs/SurrogateTestEUCTW.java \
+ sun/nio/cs/SurrogateTestHKSCS.java \
+ sun/nio/cs/TestConverterDroppedCharacters.java \
+ sun/nio/cs/TestCp93xSISO.java \
+ sun/nio/cs/TestIBM1364.java \
+ sun/nio/cs/TestIBMBugs.java \
+ sun/nio/cs/TestIllegalISO2022Esc.java \
+ sun/nio/cs/TestISO2022JP.java \
+ sun/nio/cs/TestMS5022X.java \
+ sun/nio/cs/TestSJIS0213.java \
+ sun/nio/cs/TestTrailingEscapesISO2022JP.java \
+ sun/nio/cs/TestUni2HKSCS.java \
+ sun/nio/cs/ZeroedByteArrayEUCTWTest.java
+
+# Compact 3 adds further tests to compact2
+#
+compact3 = \
+ :compact2 \
+ :needs_compact3 \
+ -:needs_jre \
+ -:needs_jdk
+
+
+# Tests that require compact3 API's
+#
+needs_compact3 = \
+ :jdk_instrument \
+ :jdk_jmx \
+ :jdk_management \
+ :jdk_sctp \
+ com/sun/jndi \
+ com/sun/org/apache/xml/internal/security \
+ com/sun/security/auth \
+ com/sun/security/sasl \
+ com/sun/security/jgss \
+ com/sun/tracing \
+ java/util/prefs \
+ javax/naming \
+ javax/security \
+ javax/smartcardio \
+ javax/sql/rowset \
+ javax/xml/crypto \
+ sun/security/acl \
+ sun/security/jgss \
+ sun/security/krb5 \
+ java/lang/System/MacEncoding/TestFileEncoding.java \
+ java/nio/channels/AsynchronousSocketChannel/Leaky.java \
+ java/security/PermissionCollection/Concurrent.java \
+ java/security/Principal/Implies.java \
+ java/security/cert/GetInstance.java \
+ java/util/logging/DrainFindDeadlockTest.java \
+ java/util/logging/LoggingMXBeanTest.java \
+ sun/net/www/http/KeepAliveCache/B5045306.java \
+ sun/security/provider/PolicyFile/Alias.java \
+ sun/security/provider/PolicyFile/Comparator.java \
+ sun/security/provider/PolicyFile/SelfWildcard.java \
+ sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLEngineImpl/SSLEngineDeadlock.java \
+ sun/security/util/Oid/OidFormat.java \
+ sun/security/util/Resources/Format.java \
+ sun/security/util/Resources/NewNamesFormat.java
+
+# Compact 2 adds full VM tests
+compact2 = \
+ :compact2_minimal \
+ :compact1 \
+ :needs_full_vm_compact2 \
+ -:needs_compact3 \
+ -:needs_jre \
+ -:needs_jdk
+
+# Tests that require compact2 API's and a full VM
+#
+needs_full_vm_compact2 =
+
+# Minimal VM on Compact 2 adds in some compact2 tests
+#
+compact2_minimal = \
+ :compact1_minimal \
+ :needs_compact2 \
+ -:needs_compact3 \
+ -:needs_jre \
+ -:needs_jdk
+
+# Tests that require compact2 API's
+#
+needs_compact2 = \
+ :jdk_rmi \
+ :jdk_time \
+ com/sun/org/apache \
+ com/sun/net/httpserver \
+ java/sql \
+ javax/sql \
+ javax/xml \
+ jdk/lambda \
+ sun/net/www/http \
+ sun/net/www/protocol/http \
+ java/io/BufferedReader/Lines.java \
+ java/lang/reflect/DefaultStaticTest/DefaultStaticInvokeTest.java \
+ java/lang/CharSequence/DefaultTest.java \
+ java/lang/IntegralPrimitiveToString.java \
+ java/lang/PrimitiveSumMinMaxTest.java \
+ java/lang/String/StringJoinTest.java \
+ java/lang/Thread/StopThrowable.java \
+ java/net/Authenticator/Deadlock.java \
+ java/net/CookieHandler/LocalHostCookie.java \
+ java/net/CookieHandler/CookieManagerTest.java \
+ java/net/CookieHandler/EmptyCookieHeader.java \
+ java/net/HttpCookie/IllegalCookieNameTest.java \
+ java/net/HttpURLConnection/UnmodifiableMaps.java \
+ java/net/HttpURLPermission/URLTest.java \
+ java/net/ResponseCache/Test.java \
+ java/net/URLClassLoader/ClassLoad.java \
+ java/net/URLClassLoader/closetest/CloseTest.java \
+ java/nio/Buffer/Chars.java \
+ java/nio/file/Files/StreamTest.java \
+ java/security/BasicPermission/Wildcard.java \
+ java/util/Arrays/ParallelPrefix.java \
+ java/util/Arrays/SetAllTest.java \
+ java/util/BitSet/BitSetStreamTest.java \
+ java/util/Collection/CollectionDefaults.java \
+ java/util/Collection/ListDefaults.java \
+ java/util/Collections/CheckedIdentityMap.java \
+ java/util/Collections/CheckedMapBash.java \
+ java/util/Collections/CheckedSetBash.java \
+ java/util/Collections/EmptyCollectionSerialization.java \
+ java/util/Collections/EmptyNavigableMap.java \
+ java/util/Collections/EmptyNavigableSet.java \
+ java/util/Collections/UnmodifiableMapEntrySet.java \
+ java/util/Comparator/BasicTest.java \
+ java/util/Comparator/TypeTest.java \
+ java/util/Iterator/IteratorDefaults.java \
+ java/util/Iterator/PrimitiveIteratorDefaults.java \
+ java/util/Map/BasicSerialization.java \
+ java/util/Map/Defaults.java \
+ java/util/Map/EntryComparators.java \
+ java/util/Optional/Basic.java \
+ java/util/Optional/BasicDouble.java \
+ java/util/Optional/BasicInt.java \
+ java/util/Optional/BasicLong.java \
+ java/util/Random/RandomStreamTest.java \
+ java/util/ResourceBundle/Bug6359330.java \
+ java/util/Spliterator/SpliteratorCharacteristics.java \
+ java/util/Spliterator/SpliteratorCollisions.java \
+ java/util/Spliterator/SpliteratorLateBindingFailFastTest.java \
+ java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java \
+ java/util/StringJoiner/MergeTest.java \
+ java/util/StringJoiner/StringJoinerTest.java \
+ java/util/concurrent/atomic/AtomicReferenceTest.java \
+ java/util/function/BinaryOperator/BasicTest.java \
+ java/util/logging/LoggerSupplierAPIsTest.java \
+ java/util/zip/ZipFile/StreamZipEntriesTest.java \
+ java/util/zip/ZipFile/DeleteTempJar.java \
+ javax/crypto/Cipher/CipherStreamClose.java \
+ sun/misc/URLClassPath/ClassnameCharTest.java \
+ sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/HttpsCreateSockTest.java \
+ sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/HttpsSocketFacTest.java
+
+# Compact 1 adds full VM tests
+#
+compact1 = \
+ :compact1_minimal \
+ :needs_full_vm_compact1 \
+ -:needs_compact2 \
+ -:needs_full_vm_compact2 \
+ -:needs_compact3 \
+ -:needs_jre \
+ -:needs_jdk
+
+# Tests that require compact1 API's and a full VM
+#
+needs_full_vm_compact1 =
+
+# All tests that run on the most minimal configuration: Minimal VM on Compact 1
+compact1_minimal = \
+ com \
+ java \
+ javax \
+ jdk \
+ lib \
+ sample \
+ sun \
+ vm \
+ -:needs_full_vm_compact1 \
+ -:needs_full_vm_compact2 \
+ -:needs_compact2 \
+ -:needs_compact3 \
+ -:needs_jre \
+ -:needs_jdk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/corba/transport/KeepAliveSockets.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8017195
+ * @summary Introduce option to setKeepAlive parameter on CORBA sockets
+ *
+ * @run main/othervm KeepAliveSockets
+ * @run main/othervm -Dcom.sun.CORBA.transport.enableTcpKeepAlive KeepAliveSockets
+ * @run main/othervm -Dcom.sun.CORBA.transport.enableTcpKeepAlive=true KeepAliveSockets
+ * @run main/othervm -Dcom.sun.CORBA.transport.enableTcpKeepAlive=false KeepAliveSockets
+ */
+
+import java.lang.*;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.channels.ServerSocketChannel;
+import java.util.*;
+import com.sun.corba.se.impl.orb.*;
+
+import com.sun.corba.se.impl.transport.*;
+
+public class KeepAliveSockets {
+
+ public static void main(String[] args) throws Exception {
+
+ boolean keepAlive = false;
+ String prop = System.getProperty("com.sun.CORBA.transport.enableTcpKeepAlive");
+ if (prop != null)
+ keepAlive = !"false".equalsIgnoreCase(prop);
+
+ DefaultSocketFactoryImpl sfImpl = new DefaultSocketFactoryImpl();
+ ORBImpl orb = new ORBImpl();
+ orb.set_parameters(null);
+ sfImpl.setORB(orb);
+
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.socket().bind(new InetSocketAddress(0));
+
+ InetSocketAddress isa = new InetSocketAddress("localhost", ssc.socket().getLocalPort());
+ Socket s = sfImpl.createSocket("ignore", isa);
+ System.out.println("Received factory socket" + s);
+ if (keepAlive != s.getKeepAlive())
+ throw new RuntimeException("KeepAlive value not honoured in CORBA socket");
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/security/Permissions.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+/* @test
+ * @bug 8008981
+ * @summary Test that selected Toolkit and Window methods/constructors do
+ * the appropriate permission check
+ * @run main/othervm Permissions
+ */
+
+import java.awt.AWTPermission;
+import java.awt.Frame;
+import java.awt.GraphicsConfiguration;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.util.ArrayList;
+import java.util.List;
+import java.security.Permission;
+
+public class Permissions {
+
+ static class MySecurityManager extends SecurityManager {
+ private List<Permission> permissionsChecked = new ArrayList<>();
+
+ static MySecurityManager install() {
+ MySecurityManager sm = new MySecurityManager();
+ System.setSecurityManager(sm);
+ return sm;
+ }
+
+ @Override
+ public void checkPermission(Permission perm) {
+ permissionsChecked.add(perm);
+ }
+
+ void prepare(String msg) {
+ System.out.println(msg);
+ permissionsChecked.clear();
+ }
+
+ /**
+ * Checks the security manager's checkPermission method was invoked
+ * to check the given permission and target name.
+ */
+ void assertChecked(Class<? extends Permission> type, String name) {
+ for (Permission perm: permissionsChecked) {
+ if (type.isInstance(perm) && perm.getName().equals(name))
+ return;
+ }
+ throw new RuntimeException(type.getName() + "(\"" + name + "\") not checked");
+ }
+ }
+
+ public static void main(String[] args) {
+ MySecurityManager sm = MySecurityManager.install();
+
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+
+ sm.prepare("Toolkit.getSystemClipboard()");
+ toolkit.getSystemClipboard();
+ sm.assertChecked(AWTPermission.class, "accessClipboard");
+
+ sm.prepare("Toolkit.getSystemEventQueue()");
+ toolkit.getSystemEventQueue();
+ sm.assertChecked(AWTPermission.class, "accessEventQueue");
+
+ sm.prepare("Toolkit.getSystemSelection()");
+ toolkit.getSystemSelection();
+ //sm.assertChecked(AWTPermission.class, "accessClipboard");
+
+ sm.prepare("Window(Frame)");
+ new Window((Frame)null);
+ sm.assertChecked(AWTPermission.class, "showWindowWithoutWarningBanner");
+
+ sm.prepare("Window(Window)");
+ new Window((Window)null);
+ sm.assertChecked(AWTPermission.class, "showWindowWithoutWarningBanner");
+
+ sm.prepare("Window(Window,GraphicsConfiguration)");
+ new Window((Window)null, (GraphicsConfiguration)null);
+ sm.assertChecked(AWTPermission.class, "showWindowWithoutWarningBanner");
+ }
+}
--- a/jdk/test/java/io/File/MaxPathLength.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/io/File/MaxPathLength.java Fri Sep 06 12:10:30 2013 -0400
@@ -22,7 +22,7 @@
*/
/* @test
- @bug 4759207 4403166 4165006 4403166 6182812 6274272
+ @bug 4759207 4403166 4165006 4403166 6182812 6274272 7160013
@summary Test to see if win32 path length can be greater than 260
*/
@@ -37,6 +37,10 @@
"areallylongfilenamethatsforsur";
private static boolean isWindows = false;
+ private final static int MAX_LENGTH = 256;
+
+ private static int counter = 0;
+
public static void main(String[] args) throws Exception {
String osName = System.getProperty("os.name");
if (osName.startsWith("Windows")) {
@@ -45,33 +49,39 @@
for (int i = 4; i < 7; i++) {
String name = fileName;
- while (name.length() < 256) {
+ while (name.length() < MAX_LENGTH) {
testLongPath (i, name, false);
testLongPath (i, name, true);
- name += "A";
+ name = getNextName(name);
}
}
// test long paths on windows
+ // And these long pathes cannot be handled on Solaris and Mac platforms
if (isWindows) {
String name = fileName;
- while (name.length() < 256) {
+ while (name.length() < MAX_LENGTH) {
testLongPath (20, name, false);
testLongPath (20, name, true);
- name += "A";
+ name = getNextName(name);
}
}
}
+ private static String getNextName(String fName) {
+ return (fName.length() < MAX_LENGTH/2) ? fName + fName
+ : fName + "A";
+ }
+
static void testLongPath(int max, String fn,
boolean tryAbsolute) throws Exception {
String[] created = new String[max];
String pathString = ".";
for (int i = 0; i < max -1; i++) {
- pathString = pathString + pathComponent;
+ pathString = pathString + pathComponent + (counter++);
created[max - 1 -i] = pathString;
+ }
- }
File dirFile = new File(pathString);
File f = new File(pathString + sep + fn);
@@ -88,9 +98,10 @@
System.err.println("Warning: Test directory structure exists already!");
return;
}
- Files.createDirectories(dirFile.toPath());
try {
+ Files.createDirectories(dirFile.toPath());
+
if (tryAbsolute)
dirFile = new File(dirFile.getCanonicalPath());
if (!dirFile.isDirectory())
@@ -99,6 +110,7 @@
if (!f.createNewFile()) {
throw new RuntimeException ("File.createNewFile() failed");
}
+
if (!f.exists())
throw new RuntimeException ("File.exists() failed");
if (!f.isFile())
@@ -107,11 +119,14 @@
throw new RuntimeException ("File.canRead() failed");
if (!f.canWrite())
throw new RuntimeException ("File.canWrite() failed");
+
if (!f.delete())
throw new RuntimeException ("File.delete() failed");
+
FileOutputStream fos = new FileOutputStream(f);
fos.write(1);
fos.close();
+
if (f.length() != 1)
throw new RuntimeException ("File.length() failed");
long time = System.currentTimeMillis();
@@ -148,30 +163,26 @@
throw new RuntimeException ("File.renameTo() failed for lenth="
+ abPath.length());
}
- return;
+ } else {
+ if (!nf.canRead())
+ throw new RuntimeException ("Renamed file is not readable");
+ if (!nf.canWrite())
+ throw new RuntimeException ("Renamed file is not writable");
+ if (nf.length() != 1)
+ throw new RuntimeException ("Renamed file's size is not correct");
+ if (!nf.renameTo(f)) {
+ created[0] = nf.getPath();
+ }
+ /* add a script to test these two if we got a regression later
+ if (!f.setReadOnly())
+ throw new RuntimeException ("File.setReadOnly() failed");
+ f.deleteOnExit();
+ */
}
- if (!nf.canRead())
- throw new RuntimeException ("Renamed file is not readable");
- if (!nf.canWrite())
- throw new RuntimeException ("Renamed file is not writable");
- if (nf.length() != 1)
- throw new RuntimeException ("Renamed file's size is not correct");
- nf.renameTo(f);
- /* add a script to test these two if we got a regression later
- if (!f.setReadOnly())
- throw new RuntimeException ("File.setReadOnly() failed");
- f.deleteOnExit();
- */
} finally {
// Clean up
for (int i = 0; i < max; i++) {
- pathString = created[i];
- // Only works with completex canonical paths
- File df = new File(pathString);
- pathString = df.getCanonicalPath();
- df = new File(pathString);
- if (!df.delete())
- System.out.printf("Delete failed->%s\n", pathString);
+ Files.deleteIfExists((new File(created[i])).toPath());
}
}
}
--- a/jdk/test/java/io/pathNames/General.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/io/pathNames/General.java Fri Sep 06 12:10:30 2013 -0400
@@ -28,6 +28,7 @@
import java.io.*;
import java.util.*;
+import java.nio.file.*;
public class General {
@@ -57,7 +58,7 @@
for (int i = 0; i < dl.length; i++) {
File f = new File(subdir, dl[i]);
File df = new File(dir, f.getPath());
- if (df.exists() && df.isFile()) {
+ if (Files.isRegularFile(df.toPath(), LinkOption.NOFOLLOW_LINKS)) {
return f.getPath();
}
}
@@ -65,7 +66,7 @@
File f = (subdir.length() == 0) ? new File(dl[i])
: new File(subdir, dl[i]);
File df = new File(dir, f.getPath());
- if (df.exists() && df.isDirectory()) {
+ if (Files.isDirectory(df.toPath(), LinkOption.NOFOLLOW_LINKS)) {
String[] dl2 = df.list();
if (dl2 != null) {
String ff = findSomeFile(dir, f.getPath(), dl2);
@@ -90,7 +91,7 @@
}
for (int i = 0; i < dl.length; i++) {
File f = new File(dir, dl[i]);
- if (f.isFile()) {
+ if (Files.isRegularFile(f.toPath(), LinkOption.NOFOLLOW_LINKS)) {
return dl[i];
}
}
@@ -127,7 +128,7 @@
}
for (int i = 0; i < dl.length; i++) {
File f = new File(d, dl[i]);
- if (f.isDirectory()) {
+ if (Files.isDirectory(f.toPath(), LinkOption.NOFOLLOW_LINKS)) {
String[] dl2 = f.list();
if (dl2 == null || dl2.length >= 250) {
/* Heuristic to avoid scanning huge directories */
@@ -314,7 +315,7 @@
/* Normal name */
if (f.exists()) {
- if (f.isDirectory() && f.list() != null) {
+ if (Files.isDirectory(f.toPath(), LinkOption.NOFOLLOW_LINKS) && f.list() != null) {
if ((n = findSomeFile(ans, create)) != null)
checkSlashes(d, create, ans + n, ask + n);
if ((n = findSomeDir(ans, create)) != null)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/Class/getField/ArrayLength.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+/**
+ * @test
+ * @bug 5047859
+ * @summary verify that for an array type class instance, getField("length")
+ * throws an exception, and getFields() does not contain a Field for
+ * 'length'
+ */
+
+import java.lang.reflect.Field;
+
+public class ArrayLength {
+ public static void main(String [] args) {
+ int failed = 0;
+
+ try {
+ new String[0].getClass().getField("length");
+ failed++;
+ System.out.println("getField(\"length\") should throw NoSuchFieldException");
+ } catch (NoSuchFieldException e) {
+ }
+ try {
+ new String[0].getClass().getDeclaredField("length");
+ failed++;
+ System.out.println("getDeclaredField(\"length\") should throw NoSuchFieldException");
+ } catch (NoSuchFieldException e) {
+ }
+
+ if (new String[0].getClass().getFields().length != 0) {
+ failed++;
+ System.out.println("getFields() for an array type should return a zero length array");
+ }
+
+ if (new String[0].getClass().getDeclaredFields().length != 0) {
+ failed++;
+ System.out.println("getDeclaredFields() for an array type should return a zero length array");
+ }
+
+ if (failed != 0)
+ throw new RuntimeException("Test failed see log for details");
+ }
+}
--- a/jdk/test/java/lang/invoke/7087570/Test7087570.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/lang/invoke/7087570/Test7087570.java Fri Sep 06 12:10:30 2013 -0400
@@ -35,20 +35,9 @@
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
+import static java.lang.invoke.MethodHandleInfo.*;
public class Test7087570 {
- // XXX may remove the following constant declarations when MethodHandleInfo is made public
- private static final int
- REF_getField = 1,
- REF_getStatic = 2,
- REF_putField = 3,
- REF_putStatic = 4,
- REF_invokeVirtual = 5,
- REF_invokeStatic = 6,
- REF_invokeSpecial = 7,
- REF_newInvokeSpecial = 8,
- REF_invokeInterface = 9,
- REF_LIMIT = 10;
private static final TestMethodData[] TESTS = new TestMethodData[] {
// field accessors
@@ -87,17 +76,17 @@
}
private static void doTest(MethodHandle mh, TestMethodData testMethod) {
- Object mhi = newMethodHandleInfo(mh);
+ MethodHandleInfo mhi = LOOKUP.revealDirect(mh);
System.out.printf("%s.%s: %s, nominal refKind: %s, actual refKind: %s\n",
testMethod.clazz.getName(), testMethod.name, testMethod.methodType,
- REF_KIND_NAMES[testMethod.referenceKind],
- REF_KIND_NAMES[getReferenceKind(mhi)]);
- assertEquals(testMethod.name, getName(mhi));
- assertEquals(testMethod.methodType, getMethodType(mhi));
- assertEquals(testMethod.declaringClass, getDeclaringClass(mhi));
+ referenceKindToString(testMethod.referenceKind),
+ referenceKindToString(mhi.getReferenceKind()));
+ assertEquals(testMethod.name, mhi.getName());
+ assertEquals(testMethod.methodType, mhi.getMethodType());
+ assertEquals(testMethod.declaringClass, mhi.getDeclaringClass());
assertEquals(testMethod.referenceKind == REF_invokeSpecial, isInvokeSpecial(mh));
- assertRefKindEquals(testMethod.referenceKind, getReferenceKind(mhi));
+ assertRefKindEquals(testMethod.referenceKind, mhi.getReferenceKind());
}
private static void testWithLookup() throws Throwable {
@@ -122,50 +111,8 @@
return methodType(void.class, clazz);
}
- private static final String[] REF_KIND_NAMES = {
- "MH::invokeBasic",
- "REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic",
- "REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial",
- "REF_newInvokeSpecial", "REF_invokeInterface"
- };
-
private static final Lookup LOOKUP = lookup();
- // XXX may remove the following reflective logic when MethodHandleInfo is made public
- private static final MethodHandle MH_IS_INVOKESPECIAL;
- private static final MethodHandle MHI_CONSTRUCTOR;
- private static final MethodHandle MHI_GET_NAME;
- private static final MethodHandle MHI_GET_METHOD_TYPE;
- private static final MethodHandle MHI_GET_DECLARING_CLASS;
- private static final MethodHandle MHI_GET_REFERENCE_KIND;
-
- static {
- try {
- // This is white box testing. Use reflection to grab private implementation bits.
- String magicName = "IMPL_LOOKUP";
- Field magicLookup = MethodHandles.Lookup.class.getDeclaredField(magicName);
- // This unit test will fail if a security manager is installed.
- magicLookup.setAccessible(true);
- // Forbidden fruit...
- Lookup directInvokeLookup = (Lookup) magicLookup.get(null);
- Class<?> mhiClass = Class.forName("java.lang.invoke.MethodHandleInfo", false, MethodHandle.class.getClassLoader());
- MH_IS_INVOKESPECIAL = directInvokeLookup
- .findVirtual(MethodHandle.class, "isInvokeSpecial", methodType(boolean.class));
- MHI_CONSTRUCTOR = directInvokeLookup
- .findConstructor(mhiClass, methodType(void.class, MethodHandle.class));
- MHI_GET_NAME = directInvokeLookup
- .findVirtual(mhiClass, "getName", methodType(String.class));
- MHI_GET_METHOD_TYPE = directInvokeLookup
- .findVirtual(mhiClass, "getMethodType", methodType(MethodType.class));
- MHI_GET_DECLARING_CLASS = directInvokeLookup
- .findVirtual(mhiClass, "getDeclaringClass", methodType(Class.class));
- MHI_GET_REFERENCE_KIND = directInvokeLookup
- .findVirtual(mhiClass, "getReferenceKind", methodType(int.class));
- } catch (ReflectiveOperationException ex) {
- throw new Error(ex);
- }
- }
-
private static class TestMethodData {
final Class<?> clazz;
final String name;
@@ -208,7 +155,9 @@
return LOOKUP.findStatic(testMethod.clazz, testMethod.name, testMethod.methodType);
case REF_invokeSpecial:
Class<?> thisClass = LOOKUP.lookupClass();
- return LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
+ MethodHandle smh = LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
+ noteInvokeSpecial(smh);
+ return smh;
case REF_newInvokeSpecial:
return LOOKUP.findConstructor(testMethod.clazz, testMethod.methodType);
default:
@@ -238,7 +187,9 @@
case REF_invokeSpecial: {
Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
Class<?> thisClass = LOOKUP.lookupClass();
- return LOOKUP.unreflectSpecial(m, thisClass);
+ MethodHandle smh = LOOKUP.unreflectSpecial(m, thisClass);
+ noteInvokeSpecial(smh);
+ return smh;
}
case REF_newInvokeSpecial: {
Constructor c = testMethod.clazz.getDeclaredConstructor(testMethod.methodType.parameterArray());
@@ -249,59 +200,20 @@
}
}
- private static Object newMethodHandleInfo(MethodHandle mh) {
- try {
- return MHI_CONSTRUCTOR.invoke(mh);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
- }
-
- private static boolean isInvokeSpecial(MethodHandle mh) {
- try {
- return (boolean) MH_IS_INVOKESPECIAL.invokeExact(mh);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
- }
-
- private static String getName(Object mhi) {
- try {
- return (String) MHI_GET_NAME.invoke(mhi);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
+ private static List<MethodHandle> specialMethodHandles = new ArrayList<>();
+ private static void noteInvokeSpecial(MethodHandle mh) {
+ specialMethodHandles.add(mh);
+ assert(isInvokeSpecial(mh));
}
-
- private static MethodType getMethodType(Object mhi) {
- try {
- return (MethodType) MHI_GET_METHOD_TYPE.invoke(mhi);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
- }
-
- private static Class<?> getDeclaringClass(Object mhi) {
- try {
- return (Class<?>) MHI_GET_DECLARING_CLASS.invoke(mhi);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
- }
-
- private static int getReferenceKind(Object mhi) {
- try {
- return (int) MHI_GET_REFERENCE_KIND.invoke(mhi);
- } catch (Throwable ex) {
- throw new Error(ex);
- }
+ private static boolean isInvokeSpecial(MethodHandle mh) {
+ return specialMethodHandles.contains(mh);
}
private static void assertRefKindEquals(int expect, int observed) {
if (expect == observed) return;
- String msg = "expected " + REF_KIND_NAMES[(int) expect] +
- " but observed " + REF_KIND_NAMES[(int) observed];
+ String msg = "expected " + referenceKindToString(expect) +
+ " but observed " + referenceKindToString(observed);
System.out.println("FAILED: " + msg);
throw new AssertionError(msg);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/RevealDirectTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,753 @@
+/*
+ * 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. 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
+ * @summary verify Lookup.revealDirect on a variety of input handles
+ * @compile -XDignore.symbol.file RevealDirectTest.java
+ * @run junit/othervm -ea -esa test.java.lang.invoke.RevealDirectTest
+ *
+ * @test
+ * @summary verify Lookup.revealDirect on a variety of input handles, with security manager
+ * @run main/othervm/policy=jtreg.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.RevealDirectTest
+ */
+
+/* To run manually:
+ * $ $JAVA8X_HOME/bin/javac -cp $JUNIT4_JAR -d ../../../.. -XDignore.symbol.file RevealDirectTest.java
+ * $ $JAVA8X_HOME/bin/java -cp $JUNIT4_JAR:../../../.. -ea -esa org.junit.runner.JUnitCore test.java.lang.invoke.RevealDirectTest
+ * $ $JAVA8X_HOME/bin/java -cp $JUNIT4_JAR:../../../.. -ea -esa -Djava.security.manager test.java.lang.invoke.RevealDirectTest
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.reflect.*;
+import java.lang.invoke.*;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+import static java.lang.invoke.MethodHandleInfo.*;
+import java.util.*;
+import static org.junit.Assert.*;
+import org.junit.*;
+
+public class RevealDirectTest {
+ public static void main(String... av) throws Throwable {
+ // Run the @Test methods explicitly, in case we don't want to use the JUnitCore driver.
+ // This appears to be necessary when running with a security manager.
+ Throwable fail = null;
+ for (Method test : RevealDirectTest.class.getDeclaredMethods()) {
+ if (!test.isAnnotationPresent(Test.class)) continue;
+ try {
+ test.invoke(new RevealDirectTest());
+ } catch (Throwable ex) {
+ if (ex instanceof InvocationTargetException)
+ ex = ex.getCause();
+ if (fail == null) fail = ex;
+ System.out.println("Testcase: "+test.getName()
+ +"("+test.getDeclaringClass().getName()
+ +"):\tCaused an ERROR");
+ System.out.println(ex);
+ ex.printStackTrace(System.out);
+ }
+ }
+ if (fail != null) throw fail;
+ }
+
+ public interface SimpleSuperInterface {
+ public abstract int getInt();
+ public static void printAll(String... args) {
+ System.out.println(Arrays.toString(args));
+ }
+ public int NICE_CONSTANT = 42;
+ }
+ public interface SimpleInterface extends SimpleSuperInterface {
+ default float getFloat() { return getInt(); }
+ public static void printAll(String[] args) {
+ System.out.println(Arrays.toString(args));
+ }
+ }
+ public static class Simple implements SimpleInterface, Cloneable {
+ public int intField;
+ public final int finalField;
+ private static String stringField;
+ public int getInt() { return NICE_CONSTANT; }
+ private static Number getNum() { return 804; }
+ public Simple clone() {
+ try {
+ return (Simple) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ Simple() { finalField = -NICE_CONSTANT; }
+ private static Lookup localLookup() { return lookup(); }
+ private static List<Member> members() { return getMembers(lookup().lookupClass()); };
+ }
+
+ static boolean VERBOSE = false;
+
+ @Test public void testSimple() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testSimple");
+ testOnMembers("testSimple", Simple.members(), Simple.localLookup());
+ }
+ @Test public void testPublicLookup() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testPublicLookup");
+ List<Member> mems = publicOnly(Simple.members());
+ Lookup pubLookup = publicLookup(), privLookup = Simple.localLookup();
+ testOnMembers("testPublicLookup/1", mems, pubLookup);
+ // reveal using publicLookup:
+ testOnMembers("testPublicLookup/2", mems, privLookup, pubLookup);
+ // lookup using publicLookup, but reveal using private:
+ testOnMembers("testPublicLookup/3", mems, pubLookup, privLookup);
+ }
+ @Test public void testPublicLookupNegative() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testPublicLookupNegative");
+ List<Member> mems = nonPublicOnly(Simple.members());
+ Lookup pubLookup = publicLookup(), privLookup = Simple.localLookup();
+ testOnMembersNoLookup("testPublicLookupNegative/1", mems, pubLookup);
+ testOnMembersNoReveal("testPublicLookupNegative/2", mems, privLookup, pubLookup);
+ testOnMembersNoReflect("testPublicLookupNegative/3", mems, privLookup, pubLookup);
+ }
+ @Test public void testJavaLangClass() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testJavaLangClass");
+ List<Member> mems = callerSensitive(false, publicOnly(getMembers(Class.class)));
+ mems = limit(20, mems);
+ testOnMembers("testJavaLangClass", mems, Simple.localLookup());
+ }
+ @Test public void testCallerSensitive() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testCallerSensitive");
+ List<Member> mems = union(getMembers(MethodHandles.class, "lookup"),
+ getMembers(Method.class, "invoke"),
+ getMembers(Field.class, "get", "set", "getLong"),
+ getMembers(Class.class));
+ mems = callerSensitive(true, publicOnly(mems));
+ mems = limit(10, mems);
+ testOnMembers("testCallerSensitive", mems, Simple.localLookup());
+ }
+ @Test public void testCallerSensitiveNegative() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testCallerSensitiveNegative");
+ List<Member> mems = union(getMembers(MethodHandles.class, "lookup"),
+ getMembers(Class.class, "forName"),
+ getMembers(Method.class, "invoke"));
+ mems = callerSensitive(true, publicOnly(mems));
+ // CS methods cannot be looked up with publicLookup
+ testOnMembersNoLookup("testCallerSensitiveNegative", mems, publicLookup());
+ }
+ @Test public void testMethodHandleNatives() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testMethodHandleNatives");
+ List<Member> mems = getMembers(MethodHandle.class, "invoke", "invokeExact");
+ testOnMembers("testMethodHandleNatives", mems, Simple.localLookup());
+ }
+ @Test public void testMethodHandleInvokes() throws Throwable {
+ if (VERBOSE) System.out.println("@Test testMethodHandleInvokes");
+ List<MethodType> types = new ArrayList<>();
+ Class<?>[] someParamTypes = { void.class, int.class, Object.class, Object[].class };
+ for (Class<?> rt : someParamTypes) {
+ for (Class<?> p0 : someParamTypes) {
+ if (p0 == void.class) { types.add(methodType(rt)); continue; }
+ for (Class<?> p1 : someParamTypes) {
+ if (p1 == void.class) { types.add(methodType(rt, p0)); continue; }
+ for (Class<?> p2 : someParamTypes) {
+ if (p2 == void.class) { types.add(methodType(rt, p0, p1)); continue; }
+ types.add(methodType(rt, p0, p1, p2));
+ }
+ }
+ }
+ }
+ List<Member> mems = union(getPolyMembers(MethodHandle.class, "invoke", types),
+ getPolyMembers(MethodHandle.class, "invokeExact", types));
+ testOnMembers("testMethodHandleInvokes/1", mems, Simple.localLookup());
+ testOnMembers("testMethodHandleInvokes/2", mems, publicLookup());
+ }
+
+ static List<Member> getPolyMembers(Class<?> cls, String name, List<MethodType> types) {
+ assert(cls == MethodHandle.class);
+ ArrayList<Member> mems = new ArrayList<>();
+ for (MethodType type : types) {
+ mems.add(new SignaturePolymorphicMethod(name, type));
+ }
+ return mems;
+ }
+ static List<Member> getMembers(Class<?> cls) {
+ return getMembers(cls, (String[]) null);
+ }
+ static List<Member> getMembers(Class<?> cls, String... onlyNames) {
+ List<String> names = (onlyNames == null || onlyNames.length == 0 ? null : Arrays.asList(onlyNames));
+ ArrayList<Member> res = new ArrayList<>();
+ for (Class<?> sup : getSupers(cls)) {
+ res.addAll(getDeclaredMembers(sup, "getDeclaredFields"));
+ res.addAll(getDeclaredMembers(sup, "getDeclaredMethods"));
+ res.addAll(getDeclaredMembers(sup, "getDeclaredConstructors"));
+ }
+ res = new ArrayList<>(new LinkedHashSet<>(res));
+ for (int i = 0; i < res.size(); i++) {
+ Member mem = res.get(i);
+ if (!canBeReached(mem, cls) ||
+ res.indexOf(mem) != i ||
+ mem.isSynthetic() ||
+ (names != null && !names.contains(mem.getName()))
+ ) {
+ res.remove(i--);
+ }
+ }
+ return res;
+ }
+ static List<Class<?>> getSupers(Class<?> cls) {
+ ArrayList<Class<?>> res = new ArrayList<>();
+ ArrayList<Class<?>> intfs = new ArrayList<>();
+ for (Class<?> sup = cls; sup != null; sup = sup.getSuperclass()) {
+ res.add(sup);
+ for (Class<?> intf : cls.getInterfaces()) {
+ if (!intfs.contains(intf))
+ intfs.add(intf);
+ }
+ }
+ for (int i = 0; i < intfs.size(); i++) {
+ for (Class<?> intf : intfs.get(i).getInterfaces()) {
+ if (!intfs.contains(intf))
+ intfs.add(intf);
+ }
+ }
+ res.addAll(intfs);
+ //System.out.println("getSupers => "+res);
+ return res;
+ }
+ static boolean hasSM() {
+ return (System.getSecurityManager() != null);
+ }
+ static List<Member> getDeclaredMembers(Class<?> cls, String accessor) {
+ Member[] mems = {};
+ Method getter = getMethod(Class.class, accessor);
+ if (hasSM()) {
+ try {
+ mems = (Member[]) invokeMethod(getter, cls);
+ } catch (SecurityException ex) {
+ //if (VERBOSE) ex.printStackTrace();
+ accessor = accessor.replace("Declared", "");
+ getter = getMethod(Class.class, accessor);
+ if (VERBOSE) System.out.println("replaced accessor: "+getter);
+ }
+ }
+ if (mems.length == 0) {
+ try {
+ mems = (Member[]) invokeMethod(getter, cls);
+ } catch (SecurityException ex) {
+ ex.printStackTrace();
+ }
+ }
+ if (VERBOSE) System.out.println(accessor+" "+cls.getName()+" => "+mems.length+" members");
+ return Arrays.asList(mems);
+ }
+ static Method getMethod(Class<?> cls, String name) {
+ try {
+ return cls.getMethod(name);
+ } catch (ReflectiveOperationException ex) {
+ throw new AssertionError(ex);
+ }
+ }
+ static Object invokeMethod(Method m, Object recv, Object... args) {
+ try {
+ return m.invoke(recv, args);
+ } catch (InvocationTargetException ex) {
+ Throwable ex2 = ex.getCause();
+ if (ex2 instanceof RuntimeException) throw (RuntimeException) ex2;
+ if (ex2 instanceof Error) throw (Error) ex2;
+ throw new AssertionError(ex);
+ } catch (ReflectiveOperationException ex) {
+ throw new AssertionError(ex);
+ }
+ }
+
+ static List<Member> limit(int len, List<Member> mems) {
+ if (mems.size() <= len) return mems;
+ return mems.subList(0, len);
+ }
+ @SafeVarargs
+ static List<Member> union(List<Member> mems, List<Member>... mem2s) {
+ for (List<Member> mem2 : mem2s) {
+ for (Member m : mem2) {
+ if (!mems.contains(m))
+ mems.add(m);
+ }
+ }
+ return mems;
+ }
+ static List<Member> callerSensitive(boolean cond, List<Member> members) {
+ for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
+ Member mem = i.next();
+ if (isCallerSensitive(mem) != cond)
+ i.remove();
+ }
+ if (members.isEmpty()) throw new AssertionError("trivial result");
+ return members;
+ }
+ static boolean isCallerSensitive(Member mem) {
+ if (!(mem instanceof AnnotatedElement)) return false;
+ AnnotatedElement ae = (AnnotatedElement) mem;
+ if (CS_CLASS != null)
+ return ae.isAnnotationPresent(sun.reflect.CallerSensitive.class);
+ for (java.lang.annotation.Annotation a : ae.getDeclaredAnnotations()) {
+ if (a.toString().contains(".CallerSensitive"))
+ return true;
+ }
+ return false;
+ }
+ static final Class<?> CS_CLASS;
+ static {
+ Class<?> c = null;
+ try {
+ c = sun.reflect.CallerSensitive.class;
+ } catch (SecurityException | LinkageError ex) {
+ }
+ CS_CLASS = c;
+ }
+ static List<Member> publicOnly(List<Member> members) {
+ return removeMods(members, Modifier.PUBLIC, 0);
+ }
+ static List<Member> nonPublicOnly(List<Member> members) {
+ return removeMods(members, Modifier.PUBLIC, -1);
+ }
+ static List<Member> removeMods(List<Member> members, int mask, int bits) {
+ int publicMods = (mask & Modifier.PUBLIC);
+ members = new ArrayList<>(members);
+ for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
+ Member mem = i.next();
+ int mods = mem.getModifiers();
+ if ((publicMods & mods) != 0 &&
+ (publicMods & mem.getDeclaringClass().getModifiers()) == 0)
+ mods -= publicMods;
+ if ((mods & mask) == (bits & mask))
+ i.remove();
+ }
+ return members;
+ }
+
+ void testOnMembers(String tname, List<Member> mems, Lookup lookup, Lookup... lookups) throws Throwable {
+ if (VERBOSE) System.out.println("testOnMembers "+mems);
+ Lookup revLookup = (lookups.length > 0) ? lookups[0] : null;
+ if (revLookup == null) revLookup = lookup;
+ Lookup refLookup = (lookups.length > 1) ? lookups[1] : null;
+ if (refLookup == null) refLookup = lookup;
+ assert(lookups.length <= 2);
+ testOnMembersImpl(tname, mems, lookup, revLookup, refLookup, NO_FAIL);
+ }
+ void testOnMembersNoLookup(String tname, List<Member> mems, Lookup lookup) throws Throwable {
+ if (VERBOSE) System.out.println("testOnMembersNoLookup "+mems);
+ testOnMembersImpl(tname, mems, lookup, null, null, FAIL_LOOKUP);
+ }
+ void testOnMembersNoReveal(String tname, List<Member> mems,
+ Lookup lookup, Lookup negLookup) throws Throwable {
+ if (VERBOSE) System.out.println("testOnMembersNoReveal "+mems);
+ testOnMembersImpl(tname, mems, lookup, negLookup, null, FAIL_REVEAL);
+ }
+ void testOnMembersNoReflect(String tname, List<Member> mems,
+ Lookup lookup, Lookup negLookup) throws Throwable {
+ if (VERBOSE) System.out.println("testOnMembersNoReflect "+mems);
+ testOnMembersImpl(tname, mems, lookup, lookup, negLookup, FAIL_REFLECT);
+ }
+ void testOnMembersImpl(String tname, List<Member> mems,
+ Lookup lookup,
+ Lookup revLookup,
+ Lookup refLookup,
+ int failureMode) throws Throwable {
+ Throwable fail = null;
+ int failCount = 0;
+ failureModeCounts = new int[FAIL_MODE_COUNT];
+ long tm0 = System.currentTimeMillis();
+ for (Member mem : mems) {
+ try {
+ testWithMember(mem, lookup, revLookup, refLookup, failureMode);
+ } catch (Throwable ex) {
+ if (fail == null) fail = ex;
+ if (++failCount > 10) { System.out.println("*** FAIL: too many failures"); break; }
+ System.out.println("*** FAIL: "+mem+" => "+ex);
+ if (VERBOSE) ex.printStackTrace(System.out);
+ }
+ }
+ long tm1 = System.currentTimeMillis();
+ System.out.printf("@Test %s executed %s tests in %d ms",
+ tname, testKinds(failureModeCounts), (tm1-tm0)).println();
+ if (fail != null) throw fail;
+ }
+ static String testKinds(int[] modes) {
+ int pos = modes[0], neg = -pos;
+ for (int n : modes) neg += n;
+ if (neg == 0) return pos + " positive";
+ String negs = "";
+ for (int n : modes) negs += "/"+n;
+ negs = negs.replaceFirst("/"+pos+"/", "");
+ negs += " negative";
+ if (pos == 0) return negs;
+ return pos + " positive, " + negs;
+ }
+ static class SignaturePolymorphicMethod implements Member { // non-reflected instance of MH.invoke*
+ final String name;
+ final MethodType type;
+ SignaturePolymorphicMethod(String name, MethodType type) {
+ this.name = name;
+ this.type = type;
+ }
+ public String toString() {
+ String typeStr = type.toString();
+ if (isVarArgs()) typeStr = typeStr.replaceFirst("\\[\\])$", "...)");
+ return (Modifier.toString(getModifiers())
+ +typeStr.substring(0, typeStr.indexOf('('))+" "
+ +getDeclaringClass().getTypeName()+"."
+ +getName()+typeStr.substring(typeStr.indexOf('(')));
+ }
+ public boolean equals(Object x) {
+ return (x instanceof SignaturePolymorphicMethod && equals((SignaturePolymorphicMethod)x));
+ }
+ public boolean equals(SignaturePolymorphicMethod that) {
+ return this.name.equals(that.name) && this.type.equals(that.type);
+ }
+ public int hashCode() {
+ return name.hashCode() * 31 + type.hashCode();
+ }
+ public Class<?> getDeclaringClass() { return MethodHandle.class; }
+ public String getName() { return name; }
+ public MethodType getMethodType() { return type; }
+ public int getModifiers() { return Modifier.PUBLIC | Modifier.FINAL | Modifier.NATIVE | SYNTHETIC; }
+ public boolean isVarArgs() { return Modifier.isTransient(getModifiers()); }
+ public boolean isSynthetic() { return true; }
+ public Class<?> getReturnType() { return type.returnType(); }
+ public Class<?>[] getParameterTypes() { return type.parameterArray(); }
+ static final int SYNTHETIC = 0x00001000;
+ }
+ static class UnreflectResult { // a tuple
+ final MethodHandle mh;
+ final Throwable ex;
+ final byte kind;
+ final Member mem;
+ final int var;
+ UnreflectResult(MethodHandle mh, byte kind, Member mem, int var) {
+ this.mh = mh;
+ this.ex = null;
+ this.kind = kind;
+ this.mem = mem;
+ this.var = var;
+ }
+ UnreflectResult(Throwable ex, byte kind, Member mem, int var) {
+ this.mh = null;
+ this.ex = ex;
+ this.kind = kind;
+ this.mem = mem;
+ this.var = var;
+ }
+ public String toString() {
+ return toInfoString()+"/v"+var;
+ }
+ public String toInfoString() {
+ return String.format("%s %s.%s:%s", MethodHandleInfo.referenceKindToString(kind),
+ mem.getDeclaringClass().getName(), name(mem), type(mem, kind));
+ }
+ static String name(Member mem) {
+ if (mem instanceof Constructor) return "<init>";
+ return mem.getName();
+ }
+ static MethodType type(Member mem, byte kind) {
+ if (mem instanceof Field) {
+ Class<?> type = ((Field)mem).getType();
+ if (kind == REF_putStatic || kind == REF_putField)
+ return methodType(void.class, type);
+ return methodType(type);
+ } else if (mem instanceof SignaturePolymorphicMethod) {
+ return ((SignaturePolymorphicMethod)mem).getMethodType();
+ }
+ Class<?>[] params = ((Executable)mem).getParameterTypes();
+ if (mem instanceof Constructor)
+ return methodType(void.class, params);
+ Class<?> type = ((Method)mem).getReturnType();
+ return methodType(type, params);
+ }
+ }
+ static UnreflectResult unreflectMember(Lookup lookup, Member mem, int variation) {
+ byte[] refKind = {0};
+ try {
+ return unreflectMemberOrThrow(lookup, mem, variation, refKind);
+ } catch (ReflectiveOperationException|SecurityException ex) {
+ return new UnreflectResult(ex, refKind[0], mem, variation);
+ }
+ }
+ static UnreflectResult unreflectMemberOrThrow(Lookup lookup, Member mem, int variation,
+ byte[] refKind) throws ReflectiveOperationException {
+ Class<?> cls = lookup.lookupClass();
+ Class<?> defc = mem.getDeclaringClass();
+ String name = mem.getName();
+ int mods = mem.getModifiers();
+ boolean isStatic = Modifier.isStatic(mods);
+ MethodHandle mh = null;
+ byte kind = 0;
+ if (mem instanceof Method) {
+ Method m = (Method) mem;
+ MethodType type = methodType(m.getReturnType(), m.getParameterTypes());
+ boolean canBeSpecial = (!isStatic &&
+ (lookup.lookupModes() & Modifier.PRIVATE) != 0 &&
+ defc.isAssignableFrom(cls) &&
+ (!defc.isInterface() || Arrays.asList(cls.getInterfaces()).contains(defc)));
+ if (variation >= 2)
+ kind = REF_invokeSpecial;
+ else if (isStatic)
+ kind = REF_invokeStatic;
+ else if (defc.isInterface())
+ kind = REF_invokeInterface;
+ else
+ kind = REF_invokeVirtual;
+ refKind[0] = kind;
+ switch (variation) {
+ case 0:
+ mh = lookup.unreflect(m);
+ break;
+ case 1:
+ if (defc == MethodHandle.class &&
+ !isStatic &&
+ m.isVarArgs() &&
+ Modifier.isFinal(mods) &&
+ Modifier.isNative(mods)) {
+ break;
+ }
+ if (isStatic)
+ mh = lookup.findStatic(defc, name, type);
+ else
+ mh = lookup.findVirtual(defc, name, type);
+ break;
+ case 2:
+ if (!canBeSpecial)
+ break;
+ mh = lookup.unreflectSpecial(m, lookup.lookupClass());
+ break;
+ case 3:
+ if (!canBeSpecial)
+ break;
+ mh = lookup.findSpecial(defc, name, type, lookup.lookupClass());
+ break;
+ }
+ } else if (mem instanceof SignaturePolymorphicMethod) {
+ SignaturePolymorphicMethod m = (SignaturePolymorphicMethod) mem;
+ MethodType type = methodType(m.getReturnType(), m.getParameterTypes());
+ kind = REF_invokeVirtual;
+ refKind[0] = kind;
+ switch (variation) {
+ case 0:
+ mh = lookup.findVirtual(defc, name, type);
+ break;
+ }
+ } else if (mem instanceof Constructor) {
+ name = "<init>"; // not used
+ Constructor<?> m = (Constructor<?>) mem;
+ MethodType type = methodType(void.class, m.getParameterTypes());
+ kind = REF_newInvokeSpecial;
+ refKind[0] = kind;
+ switch (variation) {
+ case 0:
+ mh = lookup.unreflectConstructor(m);
+ break;
+ case 1:
+ mh = lookup.findConstructor(defc, type);
+ break;
+ }
+ } else if (mem instanceof Field) {
+ Field m = (Field) mem;
+ Class<?> type = m.getType();
+ boolean canHaveSetter = !Modifier.isFinal(mods);
+ if (variation >= 2)
+ kind = (byte)(isStatic ? REF_putStatic : REF_putField);
+ else
+ kind = (byte)(isStatic ? REF_getStatic : REF_getField);
+ refKind[0] = kind;
+ switch (variation) {
+ case 0:
+ mh = lookup.unreflectGetter(m);
+ break;
+ case 1:
+ if (isStatic)
+ mh = lookup.findStaticGetter(defc, name, type);
+ else
+ mh = lookup.findGetter(defc, name, type);
+ break;
+ case 3:
+ if (!canHaveSetter)
+ break;
+ mh = lookup.unreflectSetter(m);
+ break;
+ case 2:
+ if (!canHaveSetter)
+ break;
+ if (isStatic)
+ mh = lookup.findStaticSetter(defc, name, type);
+ else
+ mh = lookup.findSetter(defc, name, type);
+ break;
+ }
+ } else {
+ throw new IllegalArgumentException(String.valueOf(mem));
+ }
+ if (mh == null)
+ // ran out of valid variations; return null to caller
+ return null;
+ return new UnreflectResult(mh, kind, mem, variation);
+ }
+ static boolean canBeReached(Member mem, Class<?> cls) {
+ Class<?> defc = mem.getDeclaringClass();
+ String name = mem.getName();
+ int mods = mem.getModifiers();
+ if (mem instanceof Constructor) {
+ name = "<init>"; // according to 292 spec.
+ }
+ if (defc == cls)
+ return true;
+ if (name.startsWith("<"))
+ return false; // only my own constructors
+ if (Modifier.isPrivate(mods))
+ return false; // only my own constructors
+ if (defc.getPackage() == cls.getPackage())
+ return true; // package access or greater OK
+ if (Modifier.isPublic(mods))
+ return true; // publics always OK
+ if (Modifier.isProtected(mods) && defc.isAssignableFrom(cls))
+ return true; // protected OK
+ return false;
+ }
+ static boolean consistent(UnreflectResult res, MethodHandleInfo info) {
+ assert(res.mh != null);
+ assertEquals(res.kind, info.getReferenceKind());
+ assertEquals(res.mem.getModifiers(), info.getModifiers());
+ assertEquals(res.mem.getDeclaringClass(), info.getDeclaringClass());
+ String expectName = res.mem.getName();
+ if (res.kind == REF_newInvokeSpecial)
+ expectName = "<init>";
+ assertEquals(expectName, info.getName());
+ MethodType expectType = res.mh.type();
+ if ((res.kind & 1) == (REF_getField & 1))
+ expectType = expectType.dropParameterTypes(0, 1);
+ if (res.kind == REF_newInvokeSpecial)
+ expectType = expectType.changeReturnType(void.class);
+ assertEquals(expectType, info.getMethodType());
+ assertEquals(res.mh.isVarargsCollector(), isVarArgs(info));
+ assertEquals(res.toInfoString(), info.toString());
+ assertEquals(res.toInfoString(), MethodHandleInfo.toString(info.getReferenceKind(), info.getDeclaringClass(), info.getName(), info.getMethodType()));
+ return true;
+ }
+ static boolean isVarArgs(MethodHandleInfo info) {
+ return info.isVarArgs();
+ }
+ static boolean consistent(Member mem, Member mem2) {
+ assertEquals(mem, mem2);
+ return true;
+ }
+ static boolean consistent(MethodHandleInfo info, MethodHandleInfo info2) {
+ assertEquals(info.getReferenceKind(), info2.getReferenceKind());
+ assertEquals(info.getModifiers(), info2.getModifiers());
+ assertEquals(info.getDeclaringClass(), info2.getDeclaringClass());
+ assertEquals(info.getName(), info2.getName());
+ assertEquals(info.getMethodType(), info2.getMethodType());
+ assertEquals(isVarArgs(info), isVarArgs(info));
+ return true;
+ }
+ static boolean consistent(MethodHandle mh, MethodHandle mh2) {
+ assertEquals(mh.type(), mh2.type());
+ assertEquals(mh.isVarargsCollector(), mh2.isVarargsCollector());
+ return true;
+ }
+ int[] failureModeCounts;
+ static final int NO_FAIL=0, FAIL_LOOKUP=1, FAIL_REVEAL=2, FAIL_REFLECT=3, FAIL_MODE_COUNT=4;
+ void testWithMember(Member mem,
+ Lookup lookup, // initial lookup of member => MH
+ Lookup revLookup, // reveal MH => info
+ Lookup refLookup, // reflect info => member
+ int failureMode) throws Throwable {
+ boolean expectEx1 = (failureMode == FAIL_LOOKUP); // testOnMembersNoLookup
+ boolean expectEx2 = (failureMode == FAIL_REVEAL); // testOnMembersNoReveal
+ boolean expectEx3 = (failureMode == FAIL_REFLECT); // testOnMembersNoReflect
+ for (int variation = 0; ; variation++) {
+ UnreflectResult res = unreflectMember(lookup, mem, variation);
+ failureModeCounts[failureMode] += 1;
+ if (variation == 0) assert(res != null);
+ if (res == null) break;
+ if (VERBOSE && variation == 0)
+ System.out.println("from "+mem.getDeclaringClass().getSimpleName());
+ MethodHandle mh = res.mh;
+ Throwable ex1 = res.ex;
+ if (VERBOSE) System.out.println(" "+variation+": "+res+" << "+(mh != null ? mh : ex1));
+ if (expectEx1 && ex1 != null)
+ continue; // this is OK; we expected that lookup to fail
+ if (expectEx1)
+ throw new AssertionError("unexpected lookup for negative test");
+ if (ex1 != null && !expectEx1) {
+ if (failureMode != NO_FAIL)
+ throw new AssertionError("unexpected lookup failure for negative test", ex1);
+ throw ex1;
+ }
+ MethodHandleInfo info;
+ try {
+ info = revLookup.revealDirect(mh);
+ if (expectEx2) throw new AssertionError("unexpected revelation for negative test");
+ } catch (Throwable ex2) {
+ if (VERBOSE) System.out.println(" "+variation+": "+res+" => "+mh.getClass().getName()+" => (EX2)"+ex2);
+ if (expectEx2)
+ continue; // this is OK; we expected the reflect to fail
+ if (failureMode != NO_FAIL)
+ throw new AssertionError("unexpected revelation failure for negative test", ex2);
+ throw ex2;
+ }
+ assert(consistent(res, info));
+ Member mem2;
+ try {
+ mem2 = info.reflectAs(Member.class, refLookup);
+ if (expectEx3) throw new AssertionError("unexpected reflection for negative test");
+ assert(!(mem instanceof SignaturePolymorphicMethod));
+ } catch (IllegalArgumentException ex3) {
+ if (VERBOSE) System.out.println(" "+variation+": "+info+" => (EX3)"+ex3);
+ if (expectEx3)
+ continue; // this is OK; we expected the reflect to fail
+ if (mem instanceof SignaturePolymorphicMethod)
+ continue; // this is OK; we cannot reflect MH.invokeExact(a,b,c)
+ if (failureMode != NO_FAIL)
+ throw new AssertionError("unexpected reflection failure for negative test", ex3);
+ throw ex3;
+ }
+ assert(consistent(mem, mem2));
+ UnreflectResult res2 = unreflectMember(lookup, mem2, variation);
+ MethodHandle mh2 = res2.mh;
+ assert(consistent(mh, mh2));
+ MethodHandleInfo info2 = lookup.revealDirect(mh2);
+ assert(consistent(info, info2));
+ assert(consistent(res, info2));
+ Member mem3;
+ if (hasSM())
+ mem3 = info2.reflectAs(Member.class, lookup);
+ else
+ mem3 = MethodHandles.reflectAs(Member.class, mh2);
+ assert(consistent(mem2, mem3));
+ if (hasSM()) {
+ try {
+ MethodHandles.reflectAs(Member.class, mh2);
+ throw new AssertionError("failed to throw on "+mem3);
+ } catch (SecurityException ex3) {
+ // OK...
+ }
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/jtreg.security.policy Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,9 @@
+/*
+ * security policy used by the test process
+ * must allow file reads so that jtreg itself can run
+ */
+
+grant {
+ // standard test activation permissions
+ permission java.io.FilePermission "*", "read";
+};
--- a/jdk/test/java/lang/management/ThreadMXBean/LockedMonitors.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/lang/management/ThreadMXBean/LockedMonitors.java Fri Sep 06 12:10:30 2013 -0400
@@ -37,7 +37,7 @@
* @build Barrier
* @build LockingThread
* @build ThreadDump
- * @run main LockedMonitors
+ * @run main/othervm LockedMonitors
*/
import java.lang.management.*;
--- a/jdk/test/java/lang/management/ThreadMXBean/LockedSynchronizers.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/lang/management/ThreadMXBean/LockedSynchronizers.java Fri Sep 06 12:10:30 2013 -0400
@@ -33,7 +33,7 @@
* @build Barrier
* @build SynchronizerLockingThread
* @build ThreadDump
- * @run main LockedSynchronizers
+ * @run main/othervm LockedSynchronizers
*/
import java.lang.management.*;
--- a/jdk/test/java/lang/management/ThreadMXBean/Locks.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/lang/management/ThreadMXBean/Locks.java Fri Sep 06 12:10:30 2013 -0400
@@ -193,16 +193,18 @@
public CheckerThread() {
super("CheckerThread");
}
+
+ private void waitForState(Thread.State state) {
+ thrsync.waitForSignal();
+ while (waiter.getState() != state) {
+ goSleep(10);
+ }
+ }
+
public void run() {
synchronized (ready) {
// wait until WaitingThread about to wait for objC
- thrsync.waitForSignal();
-
- int retryCount = 0;
- while (waiter.getState() != Thread.State.WAITING
- && retryCount++ < 500) {
- goSleep(100);
- }
+ waitForState(Thread.State.WAITING);
checkBlockedObject(waiter, objC, null, Thread.State.WAITING);
synchronized (objC) {
@@ -211,16 +213,13 @@
// wait for waiter thread to about to enter
// synchronized object ready.
- thrsync.waitForSignal();
- // give chance for waiter thread to get blocked on
- // object ready.
- goSleep(50);
+ waitForState(Thread.State.BLOCKED);
checkBlockedObject(waiter, ready, this, Thread.State.BLOCKED);
}
// wait for signal from waiting thread that it is about
// wait for objC.
- thrsync.waitForSignal();
+ waitForState(Thread.State.WAITING);
synchronized(objC) {
checkBlockedObject(waiter, objC, Thread.currentThread(), Thread.State.WAITING);
objC.notify();
--- a/jdk/test/java/lang/management/ThreadMXBean/MyOwnSynchronizer.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/lang/management/ThreadMXBean/MyOwnSynchronizer.java Fri Sep 06 12:10:30 2013 -0400
@@ -30,7 +30,7 @@
*
* @build Barrier
* @build ThreadDump
- * @run main MyOwnSynchronizer
+ * @run main/othervm MyOwnSynchronizer
*/
import java.lang.management.*;
--- a/jdk/test/java/lang/management/ThreadMXBean/SharedSynchronizer.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/lang/management/ThreadMXBean/SharedSynchronizer.java Fri Sep 06 12:10:30 2013 -0400
@@ -28,7 +28,7 @@
* in shared mode which has no owner when a thread is parked.
* @author Mandy Chung
*
- * @run main SharedSynchronizer
+ * @run main/othervm SharedSynchronizer
*/
--- a/jdk/test/java/lang/management/ThreadMXBean/SynchronizationStatistics.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/lang/management/ThreadMXBean/SynchronizationStatistics.java Fri Sep 06 12:10:30 2013 -0400
@@ -30,7 +30,7 @@
*
* @ignore 6309226
* @build Semaphore
- * @run main SynchronizationStatistics
+ * @run main/othervm SynchronizationStatistics
*/
import java.lang.management.*;
--- a/jdk/test/java/lang/management/ThreadMXBean/ThreadExecutionSynchronizer.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/lang/management/ThreadMXBean/ThreadExecutionSynchronizer.java Fri Sep 06 12:10:30 2013 -0400
@@ -23,7 +23,7 @@
/*
*
- * @summary Thiseclass is used to synchronize execution off two threads.
+ * @summary This class is used to synchronize execution of two threads.
* @author Swamy Venkataramanappa
*/
@@ -31,8 +31,8 @@
public class ThreadExecutionSynchronizer {
- private boolean waiting;
- private Semaphore semaphore;
+ private volatile boolean waiting;
+ private final Semaphore semaphore;
public ThreadExecutionSynchronizer() {
semaphore = new Semaphore(1);
--- a/jdk/test/java/lang/reflect/Generics/Probe.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/lang/reflect/Generics/Probe.java Fri Sep 06 12:10:30 2013 -0400
@@ -50,9 +50,9 @@
"java.util.HashMap$EntryIterator",
"java.util.HashMap$KeyIterator",
"java.util.HashMap$ValueIterator",
- "java.util.LinkedHashMap$EntryIterator",
- "java.util.LinkedHashMap$KeyIterator",
- "java.util.LinkedHashMap$ValueIterator"})
+ "java.util.LinkedHashMap$LinkedEntryIterator",
+ "java.util.LinkedHashMap$LinkedKeyIterator",
+ "java.util.LinkedHashMap$LinkedValueIterator"})
public class Probe {
public static void main (String... args) throws Throwable {
Classes classesAnnotation = (Probe.class).getAnnotation(Classes.class);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/IDN/UseSTD3ASCIIRules.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8023881
+ * @summary IDN.USE_STD3_ASCII_RULES option is too strict to use Unicode
+ * in IDN.toASCII
+ */
+
+import java.net.*;
+
+public class UseSTD3ASCIIRules {
+
+ public static void main(String[] args) throws Exception {
+ // Per Section 4.1, RFC 3490, if the UseSTD3ASCIIRules flag is set,
+ // then perform these checks:
+ //
+ // (a) Verify the absence of non-LDH ASCII code points; that is, the
+ // absence of 0..2C, 2E..2F, 3A..40, 5B..60, and 7B..7F.
+ //
+ // (b) Verify the absence of leading and trailing hyphen-minus; that
+ // is, the absence of U+002D at the beginning and end of the
+ // sequence.
+ String[] illegalNames = {
+ "www.example.com-",
+ "-www.example.com",
+ "-www.example.com-",
+ "www.ex\u002Cmple.com",
+ "www.ex\u007Bmple.com",
+ "www.ex\u007Fmple.com"
+ };
+
+ String[] legalNames = {
+ "www.ex-ample.com",
+ "www.ex\u002Dmple.com", // www.ex-mple.com
+ "www.ex\u007Ample.com", // www.exzmple.com
+ "www.ex\u3042mple.com", // www.xn--exmple-j43e.com
+ "www.\u3042\u3044\u3046.com", // www.xn--l8jeg.com
+ "www.\u793A\u4F8B.com" // www.xn--fsq092h.com
+ };
+
+ for (String name : illegalNames) {
+ try {
+ System.out.println("Convering illegal IDN: " + name);
+ IDN.toASCII(name, IDN.USE_STD3_ASCII_RULES);
+ throw new Exception(
+ "Expected to get IllegalArgumentException for " + name);
+ } catch (IllegalArgumentException iae) {
+ // That's the right behavior.
+ }
+ }
+
+ for (String name : legalNames) {
+ System.out.println("Convering legal IDN: " + name);
+ System.out.println("\tThe ACE form is: " +
+ IDN.toASCII(name, IDN.USE_STD3_ASCII_RULES));
+ }
+ }
+}
--- a/jdk/test/java/nio/file/Files/StreamTest.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/nio/file/Files/StreamTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -43,14 +43,13 @@
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
-import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BiPredicate;
-import java.util.stream.CloseableStream;
+import java.util.stream.Stream;
import java.util.stream.Collectors;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@@ -138,14 +137,14 @@
}
public void testBasic() {
- try (CloseableStream<Path> s = Files.list(testFolder)) {
- Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
+ try (Stream<Path> s = Files.list(testFolder)) {
+ Object[] actual = s.sorted().toArray();
assertEquals(actual, level1);
} catch (IOException ioe) {
fail("Unexpected IOException");
}
- try (CloseableStream<Path> s = Files.list(testFolder.resolve("empty"))) {
+ try (Stream<Path> s = Files.list(testFolder.resolve("empty"))) {
int count = s.mapToInt(p -> 1).reduce(0, Integer::sum);
assertEquals(count, 0, "Expect empty stream.");
} catch (IOException ioe) {
@@ -154,8 +153,8 @@
}
public void testWalk() {
- try (CloseableStream<Path> s = Files.walk(testFolder)) {
- Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
+ try (Stream<Path> s = Files.walk(testFolder)) {
+ Object[] actual = s.sorted().toArray();
assertEquals(actual, all);
} catch (IOException ioe) {
fail("Unexpected IOException");
@@ -163,9 +162,9 @@
}
public void testWalkOneLevel() {
- try (CloseableStream<Path> s = Files.walk(testFolder, 1)) {
+ try (Stream<Path> s = Files.walk(testFolder, 1)) {
Object[] actual = s.filter(path -> ! path.equals(testFolder))
- .sorted(Comparator.naturalOrder())
+ .sorted()
.toArray();
assertEquals(actual, level1);
} catch (IOException ioe) {
@@ -176,8 +175,8 @@
public void testWalkFollowLink() {
// If link is not supported, the directory structure won't have link.
// We still want to test the behavior with FOLLOW_LINKS option.
- try (CloseableStream<Path> s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) {
- Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
+ try (Stream<Path> s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) {
+ Object[] actual = s.sorted().toArray();
assertEquals(actual, all_folowLinks);
} catch (IOException ioe) {
fail("Unexpected IOException");
@@ -185,7 +184,7 @@
}
private void validateFileSystemLoopException(Path start, Path... causes) {
- try (CloseableStream<Path> s = Files.walk(start, FileVisitOption.FOLLOW_LINKS)) {
+ try (Stream<Path> s = Files.walk(start, FileVisitOption.FOLLOW_LINKS)) {
try {
int count = s.mapToInt(p -> 1).reduce(0, Integer::sum);
fail("Should got FileSystemLoopException, but got " + count + "elements.");
@@ -282,28 +281,28 @@
public void testFind() throws IOException {
PathBiPredicate pred = new PathBiPredicate((path, attrs) -> true);
- try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
+ try (Stream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
Set<Path> result = s.collect(Collectors.toCollection(TreeSet::new));
assertEquals(pred.visited(), all);
assertEquals(result.toArray(new Path[0]), pred.visited());
}
pred = new PathBiPredicate((path, attrs) -> attrs.isSymbolicLink());
- try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
+ try (Stream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
s.forEach(path -> assertTrue(Files.isSymbolicLink(path)));
assertEquals(pred.visited(), all);
}
pred = new PathBiPredicate((path, attrs) ->
path.getFileName().toString().startsWith("e"));
- try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
+ try (Stream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
s.forEach(path -> assertEquals(path.getFileName().toString(), "empty"));
assertEquals(pred.visited(), all);
}
pred = new PathBiPredicate((path, attrs) ->
path.getFileName().toString().startsWith("l") && attrs.isRegularFile());
- try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
+ try (Stream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
s.forEach(path -> fail("Expect empty stream"));
assertEquals(pred.visited(), all);
}
@@ -317,14 +316,14 @@
try {
// zero lines
assertTrue(Files.size(tmpfile) == 0, "File should be empty");
- try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) {
+ try (Stream<String> s = Files.lines(tmpfile, US_ASCII)) {
assertEquals(s.mapToInt(l -> 1).reduce(0, Integer::sum), 0, "No line expected");
}
// one line
byte[] hi = { (byte)'h', (byte)'i' };
Files.write(tmpfile, hi);
- try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) {
+ try (Stream<String> s = Files.lines(tmpfile, US_ASCII)) {
List<String> lines = s.collect(Collectors.toList());
assertTrue(lines.size() == 1, "One line expected");
assertTrue(lines.get(0).equals("hi"), "'Hi' expected");
@@ -334,7 +333,7 @@
List<String> expected = Arrays.asList("hi", "there");
Files.write(tmpfile, expected, US_ASCII);
assertTrue(Files.size(tmpfile) > 0, "File is empty");
- try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) {
+ try (Stream<String> s = Files.lines(tmpfile, US_ASCII)) {
List<String> lines = s.collect(Collectors.toList());
assertTrue(lines.equals(expected), "Unexpected lines");
}
@@ -342,7 +341,7 @@
// MalformedInputException
byte[] bad = { (byte)0xff, (byte)0xff };
Files.write(tmpfile, bad);
- try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) {
+ try (Stream<String> s = Files.lines(tmpfile, US_ASCII)) {
try {
List<String> lines = s.collect(Collectors.toList());
throw new RuntimeException("UncheckedIOException expected");
@@ -378,7 +377,7 @@
fsp.setFaultyMode(false);
Path fakeRoot = fs.getRoot();
try {
- try (CloseableStream<Path> s = Files.list(fakeRoot)) {
+ try (Stream<Path> s = Files.list(fakeRoot)) {
s.forEach(path -> assertEquals(path.getFileName().toString(), "DirectoryIteratorException"));
}
} catch (UncheckedIOException uioe) {
@@ -398,7 +397,7 @@
}
try {
- try (CloseableStream<Path> s = Files.list(fakeRoot)) {
+ try (Stream<Path> s = Files.list(fakeRoot)) {
s.forEach(path -> fail("should not get here"));
}
} catch (UncheckedIOException uioe) {
@@ -427,12 +426,12 @@
try {
fsp.setFaultyMode(false);
Path fakeRoot = fs.getRoot();
- try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir2"))) {
+ try (Stream<Path> s = Files.list(fakeRoot.resolve("dir2"))) {
// only one file
s.forEach(path -> assertEquals(path.getFileName().toString(), "IOException"));
}
- try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("empty"))) {
+ try (Stream<Path> s = Files.walk(fakeRoot.resolve("empty"))) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
// ordered as depth-first
@@ -440,13 +439,13 @@
}
fsp.setFaultyMode(true);
- try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir2"))) {
+ try (Stream<Path> s = Files.list(fakeRoot.resolve("dir2"))) {
s.forEach(path -> fail("should have caused exception"));
} catch (UncheckedIOException uioe) {
assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException);
}
- try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("empty"))) {
+ try (Stream<Path> s = Files.walk(fakeRoot.resolve("empty"))) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
fail("should not reach here due to IOException");
@@ -454,7 +453,7 @@
assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException);
}
- try (CloseableStream<Path> s = Files.walk(
+ try (Stream<Path> s = Files.walk(
fakeRoot.resolve("empty").resolve("IOException")))
{
String[] result = s.map(path -> path.getFileName().toString())
@@ -502,20 +501,20 @@
fsp.setFaultyMode(false);
Path fakeRoot = fs.getRoot();
// validate setting
- try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("empty"))) {
+ try (Stream<Path> s = Files.list(fakeRoot.resolve("empty"))) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
assertEqualsNoOrder(result, new String[] { "SecurityException", "sample" });
}
- try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir2"))) {
+ try (Stream<Path> s = Files.walk(fakeRoot.resolve("dir2"))) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
assertEqualsNoOrder(result, new String[] { "dir2", "SecurityException", "fileInSE", "file" });
}
if (supportsLinks) {
- try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir"))) {
+ try (Stream<Path> s = Files.list(fakeRoot.resolve("dir"))) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
assertEqualsNoOrder(result, new String[] { "d1", "f1", "lnDir2", "SecurityException", "lnDirSE", "lnFileSE" });
@@ -525,13 +524,13 @@
// execute test
fsp.setFaultyMode(true);
// ignore file cause SecurityException
- try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("empty"))) {
+ try (Stream<Path> s = Files.walk(fakeRoot.resolve("empty"))) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
assertEqualsNoOrder(result, new String[] { "empty", "sample" });
}
// skip folder cause SecurityException
- try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir2"))) {
+ try (Stream<Path> s = Files.walk(fakeRoot.resolve("dir2"))) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
assertEqualsNoOrder(result, new String[] { "dir2", "file" });
@@ -539,14 +538,14 @@
if (supportsLinks) {
// not following links
- try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir"))) {
+ try (Stream<Path> s = Files.walk(fakeRoot.resolve("dir"))) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
assertEqualsNoOrder(result, new String[] { "dir", "d1", "f1", "lnDir2", "lnDirSE", "lnFileSE" });
}
// following links
- try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir"), FileVisitOption.FOLLOW_LINKS)) {
+ try (Stream<Path> s = Files.walk(fakeRoot.resolve("dir"), FileVisitOption.FOLLOW_LINKS)) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
// ?? Should fileInSE show up?
@@ -556,19 +555,19 @@
}
// list instead of walk
- try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("empty"))) {
+ try (Stream<Path> s = Files.list(fakeRoot.resolve("empty"))) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
assertEqualsNoOrder(result, new String[] { "sample" });
}
- try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir2"))) {
+ try (Stream<Path> s = Files.list(fakeRoot.resolve("dir2"))) {
String[] result = s.map(path -> path.getFileName().toString())
.toArray(String[]::new);
assertEqualsNoOrder(result, new String[] { "file" });
}
// root cause SecurityException should be reported
- try (CloseableStream<Path> s = Files.walk(
+ try (Stream<Path> s = Files.walk(
fakeRoot.resolve("dir2").resolve("SecurityException")))
{
String[] result = s.map(path -> path.getFileName().toString())
@@ -579,7 +578,7 @@
}
// Walk a file cause SecurityException, we should get SE
- try (CloseableStream<Path> s = Files.walk(
+ try (Stream<Path> s = Files.walk(
fakeRoot.resolve("dir").resolve("SecurityException")))
{
String[] result = s.map(path -> path.getFileName().toString())
@@ -590,7 +589,7 @@
}
// List a file cause SecurityException, we should get SE as cannot read attribute
- try (CloseableStream<Path> s = Files.list(
+ try (Stream<Path> s = Files.list(
fakeRoot.resolve("dir2").resolve("SecurityException")))
{
String[] result = s.map(path -> path.getFileName().toString())
@@ -600,7 +599,7 @@
assertTrue(se.getCause() instanceof FaultyFileSystem.FaultyException);
}
- try (CloseableStream<Path> s = Files.list(
+ try (Stream<Path> s = Files.list(
fakeRoot.resolve("dir").resolve("SecurityException")))
{
String[] result = s.map(path -> path.getFileName().toString())
@@ -627,7 +626,7 @@
}
public void testConstructException() {
- try (CloseableStream<String> s = Files.lines(testFolder.resolve("notExist"), Charset.forName("UTF-8"))) {
+ try (Stream<String> s = Files.lines(testFolder.resolve("notExist"), Charset.forName("UTF-8"))) {
s.forEach(l -> fail("File is not even exist!"));
} catch (IOException ioe) {
assertTrue(ioe instanceof NoSuchFileException);
@@ -635,24 +634,26 @@
}
public void testClosedStream() throws IOException {
- try (CloseableStream<Path> s = Files.list(testFolder)) {
+ try (Stream<Path> s = Files.list(testFolder)) {
s.close();
- Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
- assertTrue(actual.length <= level1.length);
- }
-
- try (CloseableStream<Path> s = Files.walk(testFolder)) {
- s.close();
- Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
+ Object[] actual = s.sorted().toArray();
fail("Operate on closed stream should throw IllegalStateException");
} catch (IllegalStateException ex) {
// expected
}
- try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE,
+ try (Stream<Path> s = Files.walk(testFolder)) {
+ s.close();
+ Object[] actual = s.sorted().toArray();
+ fail("Operate on closed stream should throw IllegalStateException");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+
+ try (Stream<Path> s = Files.find(testFolder, Integer.MAX_VALUE,
(p, attr) -> true)) {
s.close();
- Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
+ Object[] actual = s.sorted().toArray();
fail("Operate on closed stream should throw IllegalStateException");
} catch (IllegalStateException ex) {
// expected
--- a/jdk/test/java/util/Arrays/SetAllTest.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/Arrays/SetAllTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -167,13 +167,13 @@
public void testStringSetNulls() {
String[] ar = new String[2];
try {
- Arrays.setAll(null, i -> "X");
+ Arrays.setAll(null, (IntFunction<String>) i -> "X");
fail("Arrays.setAll(null, foo) should throw NPE");
} catch (NullPointerException npe) {
// expected
}
try {
- Arrays.parallelSetAll(null, i -> "X");
+ Arrays.parallelSetAll(null, (IntFunction<String>) i -> "X");
fail("Arrays.parallelSetAll(null, foo) should throw NPE");
} catch (NullPointerException npe) {
// expected
--- a/jdk/test/java/util/Map/CheckRandomHashSeed.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/Map/CheckRandomHashSeed.java Fri Sep 06 12:10:30 2013 -0400
@@ -53,8 +53,6 @@
throw new Error("Error in test setup: " + (expectRandom ? "" : "not " ) + "expecting random hashSeed, but " + PROP_NAME + " is " + (propSet ? "" : "not ") + "enabled");
}
- testMap(new HashMap());
- testMap(new LinkedHashMap());
testMap(new WeakHashMap());
testMap(new Hashtable());
}
--- a/jdk/test/java/util/Map/InPlaceOpsCollisions.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/Map/InPlaceOpsCollisions.java Fri Sep 06 12:10:30 2013 -0400
@@ -25,7 +25,6 @@
* @test
* @bug 8005698
* @run main InPlaceOpsCollisions -shortrun
- * @run main/othervm -Djdk.map.randomseed=true InPlaceOpsCollisions -shortrun
* @summary Ensure overrides of in-place operations in Maps behave well with lots of collisions.
* @author Brent Christian
*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Map/MapBinToFromTreeTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8023463
+ * @summary Test the case where a bin is treeified and vice verser
+ * @run testng MapBinToFromTreeTest
+ */
+
+@Test
+public class MapBinToFromTreeTest {
+
+ // Initial capacity of map
+ // Should be >= the map capacity for treeifiying, see HashMap/ConcurrentMap.MIN_TREEIFY_CAPACITY
+ static final int INITIAL_CAPACITY = 64;
+
+ // Maximum size of map
+ // Should be > the treeify threshold, see HashMap/ConcurrentMap.TREEIFY_THRESHOLD
+ // Should be > INITIAL_CAPACITY to ensure resize occurs
+ static final int SIZE = 256;
+
+ // Load factor of map
+ // A value 1.0 will ensure that a new threshold == capacity
+ static final float LOAD_FACTOR = 1.0f;
+
+ @DataProvider(name = "maps")
+ static Object[][] mapProvider() {
+ return new Object[][] {
+ // Pass in the class name as a description for test reporting
+ // purposes
+ { HashMap.class.getName(), new HashMap(INITIAL_CAPACITY, LOAD_FACTOR) },
+ { LinkedHashMap.class.getName(), new LinkedHashMap(INITIAL_CAPACITY, LOAD_FACTOR) },
+ { ConcurrentHashMap.class.getName(), new ConcurrentHashMap(INITIAL_CAPACITY, LOAD_FACTOR) },
+ };
+ }
+
+ @Test(dataProvider = "maps")
+ public void testPutThenGet(String d, Map<HashCodeInteger, Integer> m) {
+ put(SIZE, m, (i, s) -> {
+ for (int j = 0; j < s; j++) {
+ assertEquals(m.get(new HashCodeInteger(j)).intValue(), j,
+ String.format("Map.get(%d)", j));
+ }
+ });
+ }
+
+ @Test(dataProvider = "maps")
+ public void testPutThenTraverse(String d, Map<HashCodeInteger, Integer> m) {
+ Collector<Integer, ?, ? extends Collection<Integer>> c = getCollector(m);
+
+ put(SIZE, m, (i, s) -> {
+ // Note that it is OK to collect to a Set (HashSet) as long as
+ // integer values are used since these tests only check for
+ // collisions and other tests will verify more general functionality
+ Collection<Integer> actual = m.keySet().stream().map(e -> e.value).collect(c);
+ Collection<Integer> expected = IntStream.range(0, s).boxed().collect(c);
+ assertEquals(actual, expected, "Map.keySet()");
+ });
+ }
+
+ @Test(dataProvider = "maps")
+ public void testRemoveThenGet(String d, Map<HashCodeInteger, Integer> m) {
+ put(SIZE, m, (i, s) -> { });
+
+ remove(m, (i, s) -> {
+ for (int j = i + 1; j < SIZE; j++) {
+ assertEquals(m.get(new HashCodeInteger(j)).intValue(), j,
+ String.format("Map.get(%d)", j));
+ }
+ });
+ }
+
+ @Test(dataProvider = "maps")
+ public void testRemoveThenTraverse(String d, Map<HashCodeInteger, Integer> m) {
+ put(SIZE, m, (i, s) -> { });
+
+ Collector<Integer, ?, ? extends Collection<Integer>> c = getCollector(m);
+
+ remove(m, (i, s) -> {
+ Collection<Integer> actual = m.keySet().stream().map(e -> e.value).collect(c);
+ Collection<Integer> expected = IntStream.range(i + 1, SIZE).boxed().collect(c);
+ assertEquals(actual, expected, "Map.keySet()");
+ });
+ }
+
+ @Test(dataProvider = "maps")
+ public void testUntreeifyOnResizeWithGet(String d, Map<HashCodeInteger, Integer> m) {
+ // Fill the map with 64 entries grouped into 4 buckets
+ put(INITIAL_CAPACITY, m, (i, s) -> { });
+
+ for (int i = INITIAL_CAPACITY; i < SIZE; i++) {
+ // Add further entries in the 0'th bucket so as not to disturb
+ // other buckets, entries of which may be distributed and/or
+ // the bucket untreeified on resize
+ m.put(new HashCodeInteger(i, 0), i);
+
+ for (int j = 0; j < INITIAL_CAPACITY; j++) {
+ assertEquals(m.get(new HashCodeInteger(j)).intValue(), j,
+ String.format("Map.get(%d) < INITIAL_CAPACITY", j));
+ }
+ for (int j = INITIAL_CAPACITY; j <= i; j++) {
+ assertEquals(m.get(new HashCodeInteger(j, 0)).intValue(), j,
+ String.format("Map.get(%d) >= INITIAL_CAPACITY", j));
+ }
+ }
+ }
+
+ @Test(dataProvider = "maps")
+ public void testUntreeifyOnResizeWithTraverse(String d, Map<HashCodeInteger, Integer> m) {
+ // Fill the map with 64 entries grouped into 4 buckets
+ put(INITIAL_CAPACITY, m, (i, s) -> { });
+
+ Collector<Integer, ?, ? extends Collection<Integer>> c = getCollector(m);
+
+ for (int i = INITIAL_CAPACITY; i < SIZE; i++) {
+ // Add further entries in the 0'th bucket so as not to disturb
+ // other buckets, entries of which may be distributed and/or
+ // the bucket untreeified on resize
+ m.put(new HashCodeInteger(i, 0), i);
+
+ Collection<Integer> actual = m.keySet().stream().map(e -> e.value).collect(c);
+ Collection<Integer> expected = IntStream.rangeClosed(0, i).boxed().collect(c);
+ assertEquals(actual, expected, "Key set");
+ }
+ }
+
+ Collector<Integer, ?, ? extends Collection<Integer>> getCollector(Map<?, ?> m) {
+ Collector<Integer, ?, ? extends Collection<Integer>> collector = m instanceof LinkedHashMap
+ ? Collectors.toList()
+ : Collectors.toSet();
+ return collector;
+ }
+
+ void put(int size, Map<HashCodeInteger, Integer> m, BiConsumer<Integer, Integer> c) {
+ for (int i = 0; i < size; i++) {
+ m.put(new HashCodeInteger(i), i);
+
+ c.accept(i, m.size());
+ }
+ }
+
+ void remove(Map<HashCodeInteger, Integer> m, BiConsumer<Integer, Integer> c) {
+ int size = m.size();
+ // Remove all elements thus ensuring at some point trees will be
+ // converting back to bins
+ for (int i = 0; i < size; i++) {
+ m.remove(new HashCodeInteger(i));
+
+ c.accept(i, m.size());
+ }
+ }
+
+ final static class HashCodeInteger implements Comparable<HashCodeInteger> {
+ final int value;
+
+ final int hashcode;
+
+ HashCodeInteger(int value) {
+ this(value, hash(value));
+ }
+
+ HashCodeInteger(int value, int hashcode) {
+ this.value = value;
+ this.hashcode = hashcode;
+ }
+
+ static int hash(int i) {
+ // Assuming 64 entries with keys from 0 to 63 then a map:
+ // - of capacity 64 will have 4 buckets with 16 entries per-bucket
+ // - of capacity 128 will have 8 buckets with 8 entries per-bucket
+ // - of capacity 256 will have 16 buckets with 4 entries per-bucket
+ //
+ // Re-sizing will result in re-distribution, doubling the buckets
+ // and reducing the entries by half. This will result in
+ // untreeifying when the number of entries is less than untreeify
+ // threshold (see HashMap/ConcurrentMap.UNTREEIFY_THRESHOLD)
+ return (i % 4) + (i / 4) * INITIAL_CAPACITY;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof HashCodeInteger) {
+ HashCodeInteger other = (HashCodeInteger) obj;
+ return other.value == value;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashcode;
+ }
+
+ @Override
+ public int compareTo(HashCodeInteger o) {
+ return value - o.value;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(value);
+ }
+ }
+}
--- a/jdk/test/java/util/Map/TreeBinSplitBackToEntries.java Fri Sep 06 12:04:18 2013 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +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.
- */
-
-import java.util.*;
-import java.lang.reflect.Field;
-
-/*
- * @test
- * @bug 8005698
- * @summary Test the case where TreeBin.splitTreeBin() converts a bin back to an Entry list
- * @run main TreeBinSplitBackToEntries unused
- * @author Brent Christian
- */
-
-public class TreeBinSplitBackToEntries {
- private static int EXPECTED_TREE_THRESHOLD = 16;
-
- // Easiest if this covers one bit higher then 'bit' in splitTreeBin() on the
- // call where the TreeBin is converted back to an Entry list
- private static int HASHMASK = 0x7F;
- private static boolean verbose = false;
- private static boolean fastFail = false;
- private static boolean failed = false;
-
- static void printlnIfVerbose(String msg) {
- if (verbose) {System.out.println(msg); }
- }
-
- public static void main(String[] args) {
- for (String arg : args) {
- switch(arg) {
- case "-verbose":
- verbose = true;
- break;
- case "-fastfail":
- fastFail = true;
- break;
- }
- }
- checkTreeThreshold();
- testMapHiTree();
- testMapLoTree();
- if (failed) {
- System.out.println("Test Failed");
- System.exit(1);
- } else {
- System.out.println("Test Passed");
- }
- }
-
- public static void checkTreeThreshold() {
- int threshold = -1;
- try {
- Class treeBinClass = Class.forName("java.util.HashMap$TreeBin");
- Field treeThreshold = treeBinClass.getDeclaredField("TREE_THRESHOLD");
- treeThreshold.setAccessible(true);
- threshold = treeThreshold.getInt(treeBinClass);
- } catch (ClassNotFoundException|NoSuchFieldException|IllegalAccessException e) {
- e.printStackTrace();
- throw new Error("Problem accessing TreeBin.TREE_THRESHOLD", e);
- }
- check("Expected TREE_THRESHOLD: " + EXPECTED_TREE_THRESHOLD +", found: " + threshold,
- threshold == EXPECTED_TREE_THRESHOLD);
- printlnIfVerbose("TREE_THRESHOLD: " + threshold);
- }
-
- public static void testMapHiTree() {
- Object[][] mapKeys = makeHiTreeTestData();
- testMapsForKeys(mapKeys, "hiTree");
- }
-
- public static void testMapLoTree() {
- Object[][] mapKeys = makeLoTreeTestData();
-
- testMapsForKeys(mapKeys, "loTree");
- }
-
- public static void testMapsForKeys(Object[][] mapKeys, String desc) {
- // loop through data sets
- for (Object[] keys_desc : mapKeys) {
- Map<Object, Object>[] maps = (Map<Object, Object>[]) new Map[]{
- new HashMap<>(4, 0.8f),
- new LinkedHashMap<>(4, 0.8f),
- };
- // for each map type.
- for (Map<Object, Object> map : maps) {
- Object[] keys = (Object[]) keys_desc[1];
- System.out.println(desc + ": testPutThenGet() for " + map.getClass());
- testPutThenGet(map, keys);
- }
- }
- }
-
- private static <T> void testPutThenGet(Map<T, T> map, T[] keys) {
- for (T key : keys) {
- printlnIfVerbose("put()ing 0x" + Integer.toHexString(Integer.parseInt(key.toString())) + ", hashCode=" + Integer.toHexString(key.hashCode()));
- map.put(key, key);
- }
- for (T key : keys) {
- check("key: 0x" + Integer.toHexString(Integer.parseInt(key.toString())) + " not found in resulting " + map.getClass().getSimpleName(), map.get(key) != null);
- }
- }
-
- /* Data to force a non-empty loTree in TreeBin.splitTreeBin() to be converted back
- * into an Entry list
- */
- private static Object[][] makeLoTreeTestData() {
- HashableInteger COLLIDING_OBJECTS[] = new HashableInteger[] {
- new HashableInteger( 0x23, HASHMASK),
- new HashableInteger( 0x123, HASHMASK),
- new HashableInteger( 0x323, HASHMASK),
- new HashableInteger( 0x523, HASHMASK),
-
- new HashableInteger( 0x723, HASHMASK),
- new HashableInteger( 0x923, HASHMASK),
- new HashableInteger( 0xB23, HASHMASK),
- new HashableInteger( 0xD23, HASHMASK),
-
- new HashableInteger( 0xF23, HASHMASK),
- new HashableInteger( 0xF123, HASHMASK),
- new HashableInteger( 0x1023, HASHMASK),
- new HashableInteger( 0x1123, HASHMASK),
-
- new HashableInteger( 0x1323, HASHMASK),
- new HashableInteger( 0x1523, HASHMASK),
- new HashableInteger( 0x1723, HASHMASK),
- new HashableInteger( 0x1923, HASHMASK),
-
- new HashableInteger( 0x1B23, HASHMASK),
- new HashableInteger( 0x1D23, HASHMASK),
- new HashableInteger( 0x3123, HASHMASK),
- new HashableInteger( 0x3323, HASHMASK),
- new HashableInteger( 0x3523, HASHMASK),
-
- new HashableInteger( 0x3723, HASHMASK),
- new HashableInteger( 0x1001, HASHMASK),
- new HashableInteger( 0x4001, HASHMASK),
- new HashableInteger( 0x1, HASHMASK),
- };
- return new Object[][] {
- new Object[]{"Colliding Objects", COLLIDING_OBJECTS},
- };
- }
-
- /* Data to force the hiTree in TreeBin.splitTreeBin() to be converted back
- * into an Entry list
- */
- private static Object[][] makeHiTreeTestData() {
- HashableInteger COLLIDING_OBJECTS[] = new HashableInteger[] {
- new HashableInteger( 0x1, HASHMASK),
- new HashableInteger( 0x101, HASHMASK),
- new HashableInteger( 0x301, HASHMASK),
- new HashableInteger( 0x501, HASHMASK),
- new HashableInteger( 0x701, HASHMASK),
-
- new HashableInteger( 0x1001, HASHMASK),
- new HashableInteger( 0x1101, HASHMASK),
- new HashableInteger( 0x1301, HASHMASK),
-
- new HashableInteger( 0x1501, HASHMASK),
- new HashableInteger( 0x1701, HASHMASK),
- new HashableInteger( 0x4001, HASHMASK),
- new HashableInteger( 0x4101, HASHMASK),
- new HashableInteger( 0x4301, HASHMASK),
-
- new HashableInteger( 0x4501, HASHMASK),
- new HashableInteger( 0x4701, HASHMASK),
- new HashableInteger( 0x8001, HASHMASK),
- new HashableInteger( 0x8101, HASHMASK),
-
-
- new HashableInteger( 0x8301, HASHMASK),
- new HashableInteger( 0x8501, HASHMASK),
- new HashableInteger( 0x8701, HASHMASK),
- new HashableInteger( 0x9001, HASHMASK),
-
- new HashableInteger( 0x23, HASHMASK),
- new HashableInteger( 0x123, HASHMASK),
- new HashableInteger( 0x323, HASHMASK),
- new HashableInteger( 0x523, HASHMASK),
- };
- return new Object[][] {
- new Object[]{"Colliding Objects", COLLIDING_OBJECTS},
- };
- }
-
- static void check(String desc, boolean cond) {
- if (!cond) {
- fail(desc);
- }
- }
-
- static void fail(String msg) {
- failed = true;
- (new Error("Failure: " + msg)).printStackTrace(System.err);
- if (fastFail) {
- System.exit(1);
- }
- }
-
- final static class HashableInteger implements Comparable<HashableInteger> {
- final int value;
- final int hashmask; //yes duplication
-
- HashableInteger(int value, int hashmask) {
- this.value = value;
- this.hashmask = hashmask;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof HashableInteger) {
- HashableInteger other = (HashableInteger) obj;
- return other.value == value;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- // This version ANDs the mask
- return value & hashmask;
- }
-
- @Override
- public int compareTo(HashableInteger o) {
- return value - o.value;
- }
-
- @Override
- public String toString() {
- return Integer.toString(value);
- }
- }
-}
--- a/jdk/test/java/util/Random/RandomStreamTest.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/Random/RandomStreamTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -82,13 +82,6 @@
assertEquals(destination.size(), count);
}
- @Test(dataProvider = "suppliers")
- public void testRandomGaussianStream(final Random random, final int count) {
- final List<Double> destination = new ArrayList<>(count);
- random.gaussians().limit(count).forEach(destination::add);
- assertEquals(destination.size(), count);
- }
-
@Test
public void testIntStream() {
final long seed = System.currentTimeMillis();
@@ -132,20 +125,6 @@
}
@Test
- public void testGaussianStream() {
- final long seed = System.currentTimeMillis();
- final Random r1 = new Random(seed);
- final double[] a = new double[SIZE];
- for (int i=0; i < SIZE; i++) {
- a[i] = r1.nextGaussian();
- }
-
- final Random r2 = new Random(seed); // same seed
- final double[] b = r2.gaussians().limit(SIZE).toArray();
- assertEquals(a, b);
- }
-
- @Test
public void testThreadLocalIntStream() throws InterruptedException, ExecutionException, TimeoutException {
ThreadLocalRandom tlr = ThreadLocalRandom.current();
testRandomResultSupplierConcurrently(() -> tlr.ints().limit(SIZE).boxed().collect(toList()));
@@ -163,12 +142,6 @@
testRandomResultSupplierConcurrently(() -> tlr.doubles().limit(SIZE).boxed().collect(toList()));
}
- @Test
- public void testThreadLocalGaussianStream() throws InterruptedException, ExecutionException, TimeoutException {
- ThreadLocalRandom tlr = ThreadLocalRandom.current();
- testRandomResultSupplierConcurrently(() -> tlr.gaussians().limit(SIZE).boxed().collect(toList()));
- }
-
<T> void testRandomResultSupplierConcurrently(Supplier<T> s) throws InterruptedException, ExecutionException, TimeoutException {
// Produce 10 completable future tasks
final int tasks = 10;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Random/RandomTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.LongAdder;
+import java.util.function.BiConsumer;
+
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @run testng RandomTest
+ * @summary test methods on Random
+ */
+@Test
+public class RandomTest {
+
+ // Note: this test was adapted from the 166 TCK ThreadLocalRandomTest test
+ // and modified to be a TestNG test
+
+ /*
+ * Testing coverage notes:
+ *
+ * We don't test randomness properties, but only that repeated
+ * calls, up to NCALLS tries, produce at least one different
+ * result. For bounded versions, we sample various intervals
+ * across multiples of primes.
+ */
+
+ // max numbers of calls to detect getting stuck on one value
+ static final int NCALLS = 10000;
+
+ // max sampled int bound
+ static final int MAX_INT_BOUND = (1 << 28);
+
+ // max sampled long bound
+ static final long MAX_LONG_BOUND = (1L << 42);
+
+ // Number of replications for other checks
+ static final int REPS = 20;
+
+ /**
+ * Repeated calls to nextInt produce at least two distinct results
+ */
+ public void testNextInt() {
+ Random r = new Random();
+ int f = r.nextInt();
+ int i = 0;
+ while (i < NCALLS && r.nextInt() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * Repeated calls to nextLong produce at least two distinct results
+ */
+ public void testNextLong() {
+ Random r = new Random();
+ long f = r.nextLong();
+ int i = 0;
+ while (i < NCALLS && r.nextLong() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * Repeated calls to nextBoolean produce at least two distinct results
+ */
+ public void testNextBoolean() {
+ Random r = new Random();
+ boolean f = r.nextBoolean();
+ int i = 0;
+ while (i < NCALLS && r.nextBoolean() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * Repeated calls to nextFloat produce at least two distinct results
+ */
+ public void testNextFloat() {
+ Random r = new Random();
+ float f = r.nextFloat();
+ int i = 0;
+ while (i < NCALLS && r.nextFloat() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * Repeated calls to nextDouble produce at least two distinct results
+ */
+ public void testNextDouble() {
+ Random r = new Random();
+ double f = r.nextDouble();
+ int i = 0;
+ while (i < NCALLS && r.nextDouble() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * Repeated calls to nextGaussian produce at least two distinct results
+ */
+ public void testNextGaussian() {
+ Random r = new Random();
+ double f = r.nextGaussian();
+ int i = 0;
+ while (i < NCALLS && r.nextGaussian() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * nextInt(negative) throws IllegalArgumentException
+ */
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testNextIntBoundedNeg() {
+ Random r = new Random();
+ int f = r.nextInt(-17);
+ }
+
+ /**
+ * nextInt(bound) returns 0 <= value < bound; repeated calls produce at
+ * least two distinct results
+ */
+ public void testNextIntBounded() {
+ Random r = new Random();
+ // sample bound space across prime number increments
+ for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) {
+ int f = r.nextInt(bound);
+ assertTrue(0 <= f && f < bound);
+ int i = 0;
+ int j;
+ while (i < NCALLS &&
+ (j = r.nextInt(bound)) == f) {
+ assertTrue(0 <= j && j < bound);
+ ++i;
+ }
+ assertTrue(i < NCALLS);
+ }
+ }
+
+ /**
+ * Invoking sized ints, long, doubles, with negative sizes throws
+ * IllegalArgumentException
+ */
+ public void testBadStreamSize() {
+ Random r = new Random();
+ executeAndCatchIAE(() -> r.ints(-1L));
+ executeAndCatchIAE(() -> r.ints(-1L, 2, 3));
+ executeAndCatchIAE(() -> r.longs(-1L));
+ executeAndCatchIAE(() -> r.longs(-1L, -1L, 1L));
+ executeAndCatchIAE(() -> r.doubles(-1L));
+ executeAndCatchIAE(() -> r.doubles(-1L, .5, .6));
+ }
+
+ /**
+ * Invoking bounded ints, long, doubles, with illegal bounds throws
+ * IllegalArgumentException
+ */
+ public void testBadStreamBounds() {
+ Random r = new Random();
+ executeAndCatchIAE(() -> r.ints(2, 1));
+ executeAndCatchIAE(() -> r.ints(10, 42, 42));
+ executeAndCatchIAE(() -> r.longs(-1L, -1L));
+ executeAndCatchIAE(() -> r.longs(10, 1L, -2L));
+
+ testDoubleBadOriginBound((o, b) -> r.doubles(10, o, b));
+ }
+
+ // An arbitrary finite double value
+ static final double FINITE = Math.PI;
+
+ void testDoubleBadOriginBound(BiConsumer<Double, Double> bi) {
+ executeAndCatchIAE(() -> bi.accept(17.0, 2.0));
+ executeAndCatchIAE(() -> bi.accept(0.0, 0.0));
+ executeAndCatchIAE(() -> bi.accept(Double.NaN, FINITE));
+ executeAndCatchIAE(() -> bi.accept(FINITE, Double.NaN));
+ executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
+
+ // Returns NaN
+// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, FINITE));
+// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
+
+ executeAndCatchIAE(() -> bi.accept(FINITE, Double.NEGATIVE_INFINITY));
+
+ // Returns Double.MAX_VALUE
+// executeAndCatchIAE(() -> bi.accept(FINITE, Double.POSITIVE_INFINITY));
+
+ executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
+ executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, FINITE));
+ executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
+ }
+
+ private void executeAndCatchIAE(Runnable r) {
+ executeAndCatch(IllegalArgumentException.class, r);
+ }
+
+ private void executeAndCatch(Class<? extends Exception> expected, Runnable r) {
+ Exception caught = null;
+ try {
+ r.run();
+ }
+ catch (Exception e) {
+ caught = e;
+ }
+
+ assertNotNull(caught,
+ String.format("No Exception was thrown, expected an Exception of %s to be thrown",
+ expected.getName()));
+ Assert.assertTrue(expected.isInstance(caught),
+ String.format("Exception thrown %s not an instance of %s",
+ caught.getClass().getName(), expected.getName()));
+ }
+
+ /**
+ * A sequential sized stream of ints generates the given number of values
+ */
+ public void testIntsCount() {
+ LongAdder counter = new LongAdder();
+ Random r = new Random();
+ long size = 0;
+ for (int reps = 0; reps < REPS; ++reps) {
+ counter.reset();
+ r.ints(size).forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ size += 524959;
+ }
+ }
+
+ /**
+ * A sequential sized stream of longs generates the given number of values
+ */
+ public void testLongsCount() {
+ LongAdder counter = new LongAdder();
+ Random r = new Random();
+ long size = 0;
+ for (int reps = 0; reps < REPS; ++reps) {
+ counter.reset();
+ r.longs(size).forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ size += 524959;
+ }
+ }
+
+ /**
+ * A sequential sized stream of doubles generates the given number of values
+ */
+ public void testDoublesCount() {
+ LongAdder counter = new LongAdder();
+ Random r = new Random();
+ long size = 0;
+ for (int reps = 0; reps < REPS; ++reps) {
+ counter.reset();
+ r.doubles(size).forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ size += 524959;
+ }
+ }
+
+ /**
+ * Each of a sequential sized stream of bounded ints is within bounds
+ */
+ public void testBoundedInts() {
+ AtomicInteger fails = new AtomicInteger(0);
+ Random r = new Random();
+ long size = 12345L;
+ for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) {
+ for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) {
+ final int lo = least, hi = bound;
+ r.ints(size, lo, hi).
+ forEach(x -> {
+ if (x < lo || x >= hi)
+ fails.getAndIncrement();
+ });
+ }
+ }
+ assertEquals(fails.get(), 0);
+ }
+
+ /**
+ * Each of a sequential sized stream of bounded longs is within bounds
+ */
+ public void testBoundedLongs() {
+ AtomicInteger fails = new AtomicInteger(0);
+ Random r = new Random();
+ long size = 123L;
+ for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) {
+ for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+ final long lo = least, hi = bound;
+ r.longs(size, lo, hi).
+ forEach(x -> {
+ if (x < lo || x >= hi)
+ fails.getAndIncrement();
+ });
+ }
+ }
+ assertEquals(fails.get(), 0);
+ }
+
+ /**
+ * Each of a sequential sized stream of bounded doubles is within bounds
+ */
+ public void testBoundedDoubles() {
+ AtomicInteger fails = new AtomicInteger(0);
+ Random r = new Random();
+ long size = 456;
+ for (double least = 0.00011; least < 1.0e20; least *= 9) {
+ for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) {
+ final double lo = least, hi = bound;
+ r.doubles(size, lo, hi).
+ forEach(x -> {
+ if (x < lo || x >= hi)
+ fails.getAndIncrement();
+ });
+ }
+ }
+ assertEquals(fails.get(), 0);
+ }
+
+ /**
+ * A parallel unsized stream of ints generates at least 100 values
+ */
+ public void testUnsizedIntsCount() {
+ LongAdder counter = new LongAdder();
+ Random r = new Random();
+ long size = 100;
+ r.ints().limit(size).parallel().forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+ /**
+ * A parallel unsized stream of longs generates at least 100 values
+ */
+ public void testUnsizedLongsCount() {
+ LongAdder counter = new LongAdder();
+ Random r = new Random();
+ long size = 100;
+ r.longs().limit(size).parallel().forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+ /**
+ * A parallel unsized stream of doubles generates at least 100 values
+ */
+ public void testUnsizedDoublesCount() {
+ LongAdder counter = new LongAdder();
+ Random r = new Random();
+ long size = 100;
+ r.doubles().limit(size).parallel().forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+ /**
+ * A sequential unsized stream of ints generates at least 100 values
+ */
+ public void testUnsizedIntsCountSeq() {
+ LongAdder counter = new LongAdder();
+ Random r = new Random();
+ long size = 100;
+ r.ints().limit(size).forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+ /**
+ * A sequential unsized stream of longs generates at least 100 values
+ */
+ public void testUnsizedLongsCountSeq() {
+ LongAdder counter = new LongAdder();
+ Random r = new Random();
+ long size = 100;
+ r.longs().limit(size).forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+ /**
+ * A sequential unsized stream of doubles generates at least 100 values
+ */
+ public void testUnsizedDoublesCountSeq() {
+ LongAdder counter = new LongAdder();
+ Random r = new Random();
+ long size = 100;
+ r.doubles().limit(size).forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+}
--- a/jdk/test/java/util/Spliterator/SpliteratorCharacteristics.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/Spliterator/SpliteratorCharacteristics.java Fri Sep 06 12:10:30 2013 -0400
@@ -23,7 +23,7 @@
/**
* @test
- * @bug 8020156 8020009 8022326
+ * @bug 8020156 8020009 8022326 8012913
* @run testng SpliteratorCharacteristics
*/
@@ -32,6 +32,10 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
@@ -47,7 +51,27 @@
@Test
public class SpliteratorCharacteristics {
- // TreeMap
+ public void testHashMap() {
+ assertMapCharacteristics(new HashMap<>(),
+ Spliterator.SIZED | Spliterator.DISTINCT);
+ }
+
+ public void testHashSet() {
+ assertSetCharacteristics(new HashSet<>(),
+ Spliterator.SIZED | Spliterator.DISTINCT);
+ }
+
+ public void testLinkedHashMap() {
+ assertMapCharacteristics(new LinkedHashMap<>(),
+ Spliterator.SIZED | Spliterator.DISTINCT |
+ Spliterator.ORDERED);
+ }
+
+ public void testLinkedHashSet() {
+ assertSetCharacteristics(new LinkedHashSet<>(),
+ Spliterator.SIZED | Spliterator.DISTINCT |
+ Spliterator.ORDERED);
+ }
public void testTreeMap() {
assertSortedMapCharacteristics(new TreeMap<>(),
@@ -61,9 +85,6 @@
Spliterator.SORTED | Spliterator.ORDERED);
}
-
- // TreeSet
-
public void testTreeSet() {
assertSortedSetCharacteristics(new TreeSet<>(),
Spliterator.SIZED | Spliterator.DISTINCT |
@@ -76,9 +97,6 @@
Spliterator.SORTED | Spliterator.ORDERED);
}
-
- // ConcurrentSkipListMap
-
public void testConcurrentSkipListMap() {
assertSortedMapCharacteristics(new ConcurrentSkipListMap<>(),
Spliterator.CONCURRENT | Spliterator.NONNULL |
@@ -93,9 +111,6 @@
Spliterator.ORDERED);
}
-
- // ConcurrentSkipListSet
-
public void testConcurrentSkipListSet() {
assertSortedSetCharacteristics(new ConcurrentSkipListSet<>(),
Spliterator.CONCURRENT | Spliterator.NONNULL |
@@ -113,35 +128,58 @@
//
- void assertSortedMapCharacteristics(SortedMap<Integer, String> m, int keyCharacteristics) {
+
+ void assertMapCharacteristics(Map<Integer, String> m, int keyCharacteristics) {
+ assertMapCharacteristics(m, keyCharacteristics, 0);
+ }
+
+ void assertMapCharacteristics(Map<Integer, String> m, int keyCharacteristics, int notValueCharacteristics) {
initMap(m);
- boolean hasComparator = m.comparator() != null;
+ assertCharacteristics(m.keySet(), keyCharacteristics);
+
+ assertCharacteristics(m.values(),
+ keyCharacteristics & ~(Spliterator.DISTINCT | notValueCharacteristics));
+
+ assertCharacteristics(m.entrySet(), keyCharacteristics);
+
+ if ((keyCharacteristics & Spliterator.SORTED) == 0) {
+ assertISEComparator(m.keySet());
+ assertISEComparator(m.values());
+ assertISEComparator(m.entrySet());
+ }
+ }
+
+ void assertSetCharacteristics(Set<Integer> s, int keyCharacteristics) {
+ initSet(s);
+
+ assertCharacteristics(s, keyCharacteristics);
+
+ if ((keyCharacteristics & Spliterator.SORTED) == 0) {
+ assertISEComparator(s);
+ }
+ }
+
+ void assertSortedMapCharacteristics(SortedMap<Integer, String> m, int keyCharacteristics) {
+ assertMapCharacteristics(m, keyCharacteristics, Spliterator.SORTED);
Set<Integer> keys = m.keySet();
- assertCharacteristics(keys, keyCharacteristics);
- if (hasComparator) {
+ if (m.comparator() != null) {
assertNotNullComparator(keys);
}
else {
assertNullComparator(keys);
}
- assertCharacteristics(m.values(),
- keyCharacteristics & ~(Spliterator.DISTINCT | Spliterator.SORTED));
assertISEComparator(m.values());
- assertCharacteristics(m.entrySet(), keyCharacteristics);
assertNotNullComparator(m.entrySet());
}
void assertSortedSetCharacteristics(SortedSet<Integer> s, int keyCharacteristics) {
- initSet(s);
+ assertSetCharacteristics(s, keyCharacteristics);
- boolean hasComparator = s.comparator() != null;
-
- assertCharacteristics(s, keyCharacteristics);
- if (hasComparator) {
+ if (s.comparator() != null) {
assertNotNullComparator(s);
}
else {
@@ -161,27 +199,18 @@
}
void assertCharacteristics(Collection<?> c, int expectedCharacteristics) {
- assertCharacteristics(c.spliterator(), expectedCharacteristics);
- }
-
- void assertCharacteristics(Spliterator<?> s, int expectedCharacteristics) {
- assertTrue(s.hasCharacteristics(expectedCharacteristics));
+ assertTrue(c.spliterator().hasCharacteristics(expectedCharacteristics),
+ "Spliterator characteristics");
}
void assertNullComparator(Collection<?> c) {
- assertNullComparator(c.spliterator());
- }
-
- void assertNullComparator(Spliterator<?> s) {
- assertNull(s.getComparator());
+ assertNull(c.spliterator().getComparator(),
+ "Comparator of Spliterator of Collection");
}
void assertNotNullComparator(Collection<?> c) {
- assertNotNullComparator(c.spliterator());
- }
-
- void assertNotNullComparator(Spliterator<?> s) {
- assertNotNull(s.getComparator());
+ assertNotNull(c.spliterator().getComparator(),
+ "Comparator of Spliterator of Collection");
}
void assertISEComparator(Collection<?> c) {
@@ -196,6 +225,6 @@
catch (IllegalStateException e) {
caught = true;
}
- assertTrue(caught);
+ assertTrue(caught, "Throwing IllegalStateException");
}
}
--- a/jdk/test/java/util/SplittableRandom/SplittableRandomTest.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/SplittableRandom/SplittableRandomTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -25,8 +25,10 @@
import org.testng.annotations.Test;
import java.util.SplittableRandom;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
+import java.util.function.BiConsumer;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
@@ -273,6 +275,53 @@
}
/**
+ * nextDouble(bound) throws IllegalArgumentException
+ */
+ public void testNextDoubleBadBound() {
+ SplittableRandom sr = new SplittableRandom();
+ executeAndCatchIAE(() -> sr.nextDouble(0.0));
+ executeAndCatchIAE(() -> sr.nextDouble(-0.0));
+ executeAndCatchIAE(() -> sr.nextDouble(+0.0));
+ executeAndCatchIAE(() -> sr.nextDouble(-1.0));
+ executeAndCatchIAE(() -> sr.nextDouble(Double.NaN));
+ executeAndCatchIAE(() -> sr.nextDouble(Double.NEGATIVE_INFINITY));
+
+ // Returns Double.MAX_VALUE
+// executeAndCatchIAE(() -> r.nextDouble(Double.POSITIVE_INFINITY));
+ }
+
+ /**
+ * nextDouble(origin, bound) throws IllegalArgumentException
+ */
+ public void testNextDoubleBadOriginBound() {
+ testDoubleBadOriginBound(new SplittableRandom()::nextDouble);
+ }
+
+ // An arbitrary finite double value
+ static final double FINITE = Math.PI;
+
+ void testDoubleBadOriginBound(BiConsumer<Double, Double> bi) {
+ executeAndCatchIAE(() -> bi.accept(17.0, 2.0));
+ executeAndCatchIAE(() -> bi.accept(0.0, 0.0));
+ executeAndCatchIAE(() -> bi.accept(Double.NaN, FINITE));
+ executeAndCatchIAE(() -> bi.accept(FINITE, Double.NaN));
+ executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
+
+ // Returns NaN
+// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, FINITE));
+// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
+
+ executeAndCatchIAE(() -> bi.accept(FINITE, Double.NEGATIVE_INFINITY));
+
+ // Returns Double.MAX_VALUE
+// executeAndCatchIAE(() -> bi.accept(FINITE, Double.POSITIVE_INFINITY));
+
+ executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
+ executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, FINITE));
+ executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
+ }
+
+ /**
* nextDouble(least, bound) returns least <= value < bound;
* repeated calls produce at least two distinct results
*/
@@ -318,8 +367,8 @@
executeAndCatchIAE(() -> r.ints(10, 42, 42));
executeAndCatchIAE(() -> r.longs(-1L, -1L));
executeAndCatchIAE(() -> r.longs(10, 1L, -2L));
- executeAndCatchIAE(() -> r.doubles(0.0, 0.0));
- executeAndCatchIAE(() -> r.doubles(10, .5, .4));
+
+ testDoubleBadOriginBound((o, b) -> r.doubles(10, o, b));
}
private void executeAndCatchIAE(Runnable r) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/ThreadLocalRandom/ThreadLocalRandomTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,559 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.LongAdder;
+import java.util.function.BiConsumer;
+
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @run testng ThreadLocalRandomTest
+ * @summary test methods on ThreadLocalRandom
+ */
+@Test
+public class ThreadLocalRandomTest {
+
+ // Note: this test was copied from the 166 TCK ThreadLocalRandomTest test
+ // and modified to be a TestNG test
+
+ /*
+ * Testing coverage notes:
+ *
+ * We don't test randomness properties, but only that repeated
+ * calls, up to NCALLS tries, produce at least one different
+ * result. For bounded versions, we sample various intervals
+ * across multiples of primes.
+ */
+
+ // max numbers of calls to detect getting stuck on one value
+ static final int NCALLS = 10000;
+
+ // max sampled int bound
+ static final int MAX_INT_BOUND = (1 << 28);
+
+ // max sampled long bound
+ static final long MAX_LONG_BOUND = (1L << 42);
+
+ // Number of replications for other checks
+ static final int REPS = 20;
+
+ /**
+ * setSeed throws UnsupportedOperationException
+ */
+ @Test(expectedExceptions = UnsupportedOperationException.class)
+ public void testSetSeed() {
+ ThreadLocalRandom.current().setSeed(17);
+ }
+
+ /**
+ * Repeated calls to nextInt produce at least two distinct results
+ */
+ public void testNextInt() {
+ int f = ThreadLocalRandom.current().nextInt();
+ int i = 0;
+ while (i < NCALLS && ThreadLocalRandom.current().nextInt() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * Repeated calls to nextLong produce at least two distinct results
+ */
+ public void testNextLong() {
+ long f = ThreadLocalRandom.current().nextLong();
+ int i = 0;
+ while (i < NCALLS && ThreadLocalRandom.current().nextLong() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * Repeated calls to nextBoolean produce at least two distinct results
+ */
+ public void testNextBoolean() {
+ boolean f = ThreadLocalRandom.current().nextBoolean();
+ int i = 0;
+ while (i < NCALLS && ThreadLocalRandom.current().nextBoolean() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * Repeated calls to nextFloat produce at least two distinct results
+ */
+ public void testNextFloat() {
+ float f = ThreadLocalRandom.current().nextFloat();
+ int i = 0;
+ while (i < NCALLS && ThreadLocalRandom.current().nextFloat() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * Repeated calls to nextDouble produce at least two distinct results
+ */
+ public void testNextDouble() {
+ double f = ThreadLocalRandom.current().nextDouble();
+ int i = 0;
+ while (i < NCALLS && ThreadLocalRandom.current().nextDouble() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * Repeated calls to nextGaussian produce at least two distinct results
+ */
+ public void testNextGaussian() {
+ double f = ThreadLocalRandom.current().nextGaussian();
+ int i = 0;
+ while (i < NCALLS && ThreadLocalRandom.current().nextGaussian() == f)
+ ++i;
+ assertTrue(i < NCALLS);
+ }
+
+ /**
+ * nextInt(negative) throws IllegalArgumentException
+ */
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testNextIntBoundedNeg() {
+ int f = ThreadLocalRandom.current().nextInt(-17);
+ }
+
+ /**
+ * nextInt(least >= bound) throws IllegalArgumentException
+ */
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testNextIntBadBounds() {
+ int f = ThreadLocalRandom.current().nextInt(17, 2);
+ }
+
+ /**
+ * nextInt(bound) returns 0 <= value < bound; repeated calls produce at
+ * least two distinct results
+ */
+ public void testNextIntBounded() {
+ // sample bound space across prime number increments
+ for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) {
+ int f = ThreadLocalRandom.current().nextInt(bound);
+ assertTrue(0 <= f && f < bound);
+ int i = 0;
+ int j;
+ while (i < NCALLS &&
+ (j = ThreadLocalRandom.current().nextInt(bound)) == f) {
+ assertTrue(0 <= j && j < bound);
+ ++i;
+ }
+ assertTrue(i < NCALLS);
+ }
+ }
+
+ /**
+ * nextInt(least, bound) returns least <= value < bound; repeated calls
+ * produce at least two distinct results
+ */
+ public void testNextIntBounded2() {
+ for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) {
+ for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) {
+ int f = ThreadLocalRandom.current().nextInt(least, bound);
+ assertTrue(least <= f && f < bound);
+ int i = 0;
+ int j;
+ while (i < NCALLS &&
+ (j = ThreadLocalRandom.current().nextInt(least, bound)) == f) {
+ assertTrue(least <= j && j < bound);
+ ++i;
+ }
+ assertTrue(i < NCALLS);
+ }
+ }
+ }
+
+ /**
+ * nextLong(negative) throws IllegalArgumentException
+ */
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testNextLongBoundedNeg() {
+ long f = ThreadLocalRandom.current().nextLong(-17);
+ }
+
+ /**
+ * nextLong(least >= bound) throws IllegalArgumentException
+ */
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testNextLongBadBounds() {
+ long f = ThreadLocalRandom.current().nextLong(17, 2);
+ }
+
+ /**
+ * nextLong(bound) returns 0 <= value < bound; repeated calls produce at
+ * least two distinct results
+ */
+ public void testNextLongBounded() {
+ for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) {
+ long f = ThreadLocalRandom.current().nextLong(bound);
+ assertTrue(0 <= f && f < bound);
+ int i = 0;
+ long j;
+ while (i < NCALLS &&
+ (j = ThreadLocalRandom.current().nextLong(bound)) == f) {
+ assertTrue(0 <= j && j < bound);
+ ++i;
+ }
+ assertTrue(i < NCALLS);
+ }
+ }
+
+ /**
+ * nextLong(least, bound) returns least <= value < bound; repeated calls
+ * produce at least two distinct results
+ */
+ public void testNextLongBounded2() {
+ for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) {
+ for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+ long f = ThreadLocalRandom.current().nextLong(least, bound);
+ assertTrue(least <= f && f < bound);
+ int i = 0;
+ long j;
+ while (i < NCALLS &&
+ (j = ThreadLocalRandom.current().nextLong(least, bound)) == f) {
+ assertTrue(least <= j && j < bound);
+ ++i;
+ }
+ assertTrue(i < NCALLS);
+ }
+ }
+ }
+
+ /**
+ * nextDouble(bound) throws IllegalArgumentException
+ */
+ public void testNextDoubleBadBound() {
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ executeAndCatchIAE(() -> r.nextDouble(0.0));
+ executeAndCatchIAE(() -> r.nextDouble(-0.0));
+ executeAndCatchIAE(() -> r.nextDouble(+0.0));
+ executeAndCatchIAE(() -> r.nextDouble(-1.0));
+ executeAndCatchIAE(() -> r.nextDouble(Double.NaN));
+ executeAndCatchIAE(() -> r.nextDouble(Double.NEGATIVE_INFINITY));
+
+ // Returns Double.MAX_VALUE
+// executeAndCatchIAE(() -> r.nextDouble(Double.POSITIVE_INFINITY));
+ }
+
+ /**
+ * nextDouble(origin, bound) throws IllegalArgumentException
+ */
+ public void testNextDoubleBadOriginBound() {
+ testDoubleBadOriginBound(ThreadLocalRandom.current()::nextDouble);
+ }
+
+ // An arbitrary finite double value
+ static final double FINITE = Math.PI;
+
+ void testDoubleBadOriginBound(BiConsumer<Double, Double> bi) {
+ executeAndCatchIAE(() -> bi.accept(17.0, 2.0));
+ executeAndCatchIAE(() -> bi.accept(0.0, 0.0));
+ executeAndCatchIAE(() -> bi.accept(Double.NaN, FINITE));
+ executeAndCatchIAE(() -> bi.accept(FINITE, Double.NaN));
+ executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
+
+ // Returns NaN
+// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, FINITE));
+// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
+
+ executeAndCatchIAE(() -> bi.accept(FINITE, Double.NEGATIVE_INFINITY));
+
+ // Returns Double.MAX_VALUE
+// executeAndCatchIAE(() -> bi.accept(FINITE, Double.POSITIVE_INFINITY));
+
+ executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
+ executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, FINITE));
+ executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
+ }
+
+ /**
+ * nextDouble(least, bound) returns least <= value < bound; repeated calls
+ * produce at least two distinct results
+ */
+ public void testNextDoubleBounded2() {
+ for (double least = 0.0001; least < 1.0e20; least *= 8) {
+ for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) {
+ double f = ThreadLocalRandom.current().nextDouble(least, bound);
+ assertTrue(least <= f && f < bound);
+ int i = 0;
+ double j;
+ while (i < NCALLS &&
+ (j = ThreadLocalRandom.current().nextDouble(least, bound)) == f) {
+ assertTrue(least <= j && j < bound);
+ ++i;
+ }
+ assertTrue(i < NCALLS);
+ }
+ }
+ }
+
+ /**
+ * Invoking sized ints, long, doubles, with negative sizes throws
+ * IllegalArgumentException
+ */
+ public void testBadStreamSize() {
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ executeAndCatchIAE(() -> r.ints(-1L));
+ executeAndCatchIAE(() -> r.ints(-1L, 2, 3));
+ executeAndCatchIAE(() -> r.longs(-1L));
+ executeAndCatchIAE(() -> r.longs(-1L, -1L, 1L));
+ executeAndCatchIAE(() -> r.doubles(-1L));
+ executeAndCatchIAE(() -> r.doubles(-1L, .5, .6));
+ }
+
+ /**
+ * Invoking bounded ints, long, doubles, with illegal bounds throws
+ * IllegalArgumentException
+ */
+ public void testBadStreamBounds() {
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ executeAndCatchIAE(() -> r.ints(2, 1));
+ executeAndCatchIAE(() -> r.ints(10, 42, 42));
+ executeAndCatchIAE(() -> r.longs(-1L, -1L));
+ executeAndCatchIAE(() -> r.longs(10, 1L, -2L));
+
+ testDoubleBadOriginBound((o, b) -> r.doubles(10, o, b));
+ }
+
+ private void executeAndCatchIAE(Runnable r) {
+ executeAndCatch(IllegalArgumentException.class, r);
+ }
+
+ private void executeAndCatch(Class<? extends Exception> expected, Runnable r) {
+ Exception caught = null;
+ try {
+ r.run();
+ }
+ catch (Exception e) {
+ caught = e;
+ }
+
+ assertNotNull(caught,
+ String.format("No Exception was thrown, expected an Exception of %s to be thrown",
+ expected.getName()));
+ Assert.assertTrue(expected.isInstance(caught),
+ String.format("Exception thrown %s not an instance of %s",
+ caught.getClass().getName(), expected.getName()));
+ }
+
+ /**
+ * A parallel sized stream of ints generates the given number of values
+ */
+ public void testIntsCount() {
+ LongAdder counter = new LongAdder();
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 0;
+ for (int reps = 0; reps < REPS; ++reps) {
+ counter.reset();
+ r.ints(size).parallel().forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ size += 524959;
+ }
+ }
+
+ /**
+ * A parallel sized stream of longs generates the given number of values
+ */
+ public void testLongsCount() {
+ LongAdder counter = new LongAdder();
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 0;
+ for (int reps = 0; reps < REPS; ++reps) {
+ counter.reset();
+ r.longs(size).parallel().forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ size += 524959;
+ }
+ }
+
+ /**
+ * A parallel sized stream of doubles generates the given number of values
+ */
+ public void testDoublesCount() {
+ LongAdder counter = new LongAdder();
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 0;
+ for (int reps = 0; reps < REPS; ++reps) {
+ counter.reset();
+ r.doubles(size).parallel().forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ size += 524959;
+ }
+ }
+
+ /**
+ * Each of a parallel sized stream of bounded ints is within bounds
+ */
+ public void testBoundedInts() {
+ AtomicInteger fails = new AtomicInteger(0);
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 12345L;
+ for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) {
+ for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) {
+ final int lo = least, hi = bound;
+ r.ints(size, lo, hi).parallel().
+ forEach(x -> {
+ if (x < lo || x >= hi)
+ fails.getAndIncrement();
+ });
+ }
+ }
+ assertEquals(fails.get(), 0);
+ }
+
+ /**
+ * Each of a parallel sized stream of bounded longs is within bounds
+ */
+ public void testBoundedLongs() {
+ AtomicInteger fails = new AtomicInteger(0);
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 123L;
+ for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) {
+ for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+ final long lo = least, hi = bound;
+ r.longs(size, lo, hi).parallel().
+ forEach(x -> {
+ if (x < lo || x >= hi)
+ fails.getAndIncrement();
+ });
+ }
+ }
+ assertEquals(fails.get(), 0);
+ }
+
+ /**
+ * Each of a parallel sized stream of bounded doubles is within bounds
+ */
+ public void testBoundedDoubles() {
+ AtomicInteger fails = new AtomicInteger(0);
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 456;
+ for (double least = 0.00011; least < 1.0e20; least *= 9) {
+ for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) {
+ final double lo = least, hi = bound;
+ r.doubles(size, lo, hi).parallel().
+ forEach(x -> {
+ if (x < lo || x >= hi)
+ fails.getAndIncrement();
+ });
+ }
+ }
+ assertEquals(fails.get(), 0);
+ }
+
+ /**
+ * A parallel unsized stream of ints generates at least 100 values
+ */
+ public void testUnsizedIntsCount() {
+ LongAdder counter = new LongAdder();
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 100;
+ r.ints().limit(size).parallel().forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+ /**
+ * A parallel unsized stream of longs generates at least 100 values
+ */
+ public void testUnsizedLongsCount() {
+ LongAdder counter = new LongAdder();
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 100;
+ r.longs().limit(size).parallel().forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+ /**
+ * A parallel unsized stream of doubles generates at least 100 values
+ */
+ public void testUnsizedDoublesCount() {
+ LongAdder counter = new LongAdder();
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 100;
+ r.doubles().limit(size).parallel().forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+ /**
+ * A sequential unsized stream of ints generates at least 100 values
+ */
+ public void testUnsizedIntsCountSeq() {
+ LongAdder counter = new LongAdder();
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 100;
+ r.ints().limit(size).forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+ /**
+ * A sequential unsized stream of longs generates at least 100 values
+ */
+ public void testUnsizedLongsCountSeq() {
+ LongAdder counter = new LongAdder();
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 100;
+ r.longs().limit(size).forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+ /**
+ * A sequential unsized stream of doubles generates at least 100 values
+ */
+ public void testUnsizedDoublesCountSeq() {
+ LongAdder counter = new LongAdder();
+ ThreadLocalRandom r = ThreadLocalRandom.current();
+ long size = 100;
+ r.doubles().limit(size).forEach(x -> {
+ counter.increment();
+ });
+ assertEquals(counter.sum(), size);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/LocalizedLevelName.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+import java.util.*;
+import java.util.logging.*;
+
+/*
+ * @test
+ * @bug 8016127 8024131
+ * @summary test logging.properties localized
+ * @run main/othervm LocalizedLevelName
+ */
+
+public class LocalizedLevelName {
+ private static Object[] namesMap = {
+ "SEVERE", Locale.ENGLISH, "Severe", Level.SEVERE,
+ "WARNING", Locale.FRENCH, "Avertissement", Level.WARNING,
+ "INFO", Locale.ITALIAN, "Informazioni", Level.INFO,
+ "SEVERE", Locale.FRENCH, "Grave", Level.SEVERE,
+ "CONFIG", Locale.GERMAN, "Konfiguration", Level.CONFIG,
+ "ALL", Locale.ROOT, "All", Level.ALL,
+ "SEVERE", Locale.ROOT, "Severe", Level.SEVERE,
+ "WARNING", Locale.ROOT, "Warning", Level.WARNING,
+ "CONFIG", Locale.ROOT, "Config", Level.CONFIG,
+ "INFO", Locale.ROOT, "Info", Level.INFO,
+ "FINE", Locale.ROOT, "Fine", Level.FINE,
+ "FINER", Locale.ROOT, "Finer", Level.FINER,
+ "FINEST", Locale.ROOT, "Finest", Level.FINEST
+ };
+
+ public static void main(String args[]) throws Exception {
+ Locale defaultLocale = Locale.getDefault();
+ for (int i=0; i<namesMap.length; i += 4) {
+ final String key = (String) namesMap[i];
+ final Locale locale = (Locale) namesMap[i+1];
+ final String expectedTranslation = (String) namesMap[i+2];
+ final Level level = (Level) namesMap[i+3];
+
+ final String en = getLocalizedMessage(Locale.ENGLISH, key);
+ final String other = getLocalizedMessage(locale, key);
+
+ System.out.println(locale + ": " + key + "=" + expectedTranslation
+ + ", (Level." + level.getName() + ")");
+ System.out.println(" => localized(" + Locale.ENGLISH + ", "
+ + key + ")=" + en);
+ System.out.println(" => localized(" + locale + ", " + key
+ + ")=" + other);
+ if (!key.equals(en.toUpperCase(Locale.ROOT))) {
+ throw new RuntimeException("Expect " + key
+ + " equals upperCase(" + en + ")");
+ }
+ if (!Locale.ENGLISH.equals(locale) && !Locale.ROOT.equals(locale)
+ && key.equals(other.toUpperCase(Locale.ROOT))) {
+ throw new RuntimeException("Expect " + key
+ + " not equals upperCase(" + other +")");
+ }
+ if ((Locale.ENGLISH.equals(locale) || Locale.ROOT.equals(locale))
+ && !key.equals(other.toUpperCase(Locale.ROOT))) {
+ throw new RuntimeException("Expect " + key
+ + " equals upperCase(" + other +")");
+ }
+ if (!other.equals(expectedTranslation)) {
+ throw new RuntimeException("Expected \"" + expectedTranslation
+ + "\" for '" + locale + "' but got \"" + other + "\"");
+ }
+ Locale.setDefault(locale);
+ final String levelName = level.getLocalizedName();
+ System.out.println("Level.getLocalizedName() is: " + levelName);
+ if (!levelName.equals(other.toUpperCase(locale))) {
+ throw new RuntimeException("Expected \""
+ + other.toUpperCase(locale) + "\" for '"
+ + locale + "' but got \"" + levelName + "\"");
+ }
+ Locale.setDefault(defaultLocale);
+ }
+ }
+
+ private static final String RBNAME = "sun.util.logging.resources.logging";
+ private static String getLocalizedMessage(Locale locale, String key) {
+ ResourceBundle rb = ResourceBundle.getBundle(RBNAME, locale);
+ return rb.getString(key);
+ }
+}
--- a/jdk/test/java/util/logging/TestAppletLoggerContext.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/logging/TestAppletLoggerContext.java Fri Sep 06 12:10:30 2013 -0400
@@ -110,28 +110,19 @@
}
TestExc exc;
- TestExc global = new TestExc();
@Override
- public Object getContext() { return active ? global : null; }
- @Override
- public Object getExecutionContext() { return active ? exc : null; }
+ public Object getAppletContext() { return active ? exc : null; }
@Override
- public Object get(Object o, Object o1) { return TestExc.exc(o).get(o1); }
- @Override
- public void put(Object o, Object o1, Object o2) { TestExc.exc(o).put(o1, o2); }
+ public Object get(Object o) { return exc.get(o); }
@Override
- public void remove(Object o, Object o1) { TestExc.exc(o).remove(o1); }
- @Override
- public Object get(Object o) { return global.get(o); }
+ public void put(Object o, Object o1) { exc.put(o, o1); }
@Override
- public void put(Object o, Object o1) { global.put(o, o1); }
- @Override
- public void remove(Object o) { global.remove(o); }
+ public void remove(Object o) { exc.remove(o); }
@Override
public boolean isDisposed() { return false; }
@Override
- public boolean isMainAppContext() { return exc == null; }
+ public boolean isMainAppContext() { return !active || exc == null; }
}
final static JavaAWTAccessStub javaAwtAccess = new JavaAWTAccessStub();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/TestLoggingWithMainAppContext.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.logging.Logger;
+import javax.imageio.ImageIO;
+
+/**
+ * @test
+ * @bug 8019853 8023258
+ * @summary Test that the default user context is used when in the main
+ * application context. This test must not be run in same VM or agent
+ * VM mode: it would not test the intended behavior.
+ * @run main/othervm TestLoggingWithMainAppContext
+ */
+public class TestLoggingWithMainAppContext {
+
+ public static void main(String[] args) throws IOException {
+ System.out.println("Creating loggers.");
+
+ // These loggers will be created in the default user context.
+ final Logger foo1 = Logger.getLogger( "foo" );
+ final Logger bar1 = Logger.getLogger( "foo.bar" );
+ if (bar1.getParent() != foo1) {
+ throw new RuntimeException("Parent logger of bar1 "+bar1+" is not "+foo1);
+ }
+ System.out.println("bar1.getParent() is the same as foo1");
+
+ // Set a security manager
+ System.setSecurityManager(new SecurityManager());
+ System.out.println("Now running with security manager");
+
+ // Triggers the creation of the main AppContext
+ ByteArrayInputStream is = new ByteArrayInputStream(new byte[] { 0, 1 });
+ ImageIO.read(is); // triggers calls to system loggers & creation of main AppContext
+
+ // verify that we're still using the default user context
+ final Logger bar2 = Logger.getLogger( "foo.bar" );
+ if (bar1 != bar2) {
+ throw new RuntimeException("bar2 "+bar2+" is not the same as bar1 "+bar1);
+ }
+ System.out.println("bar2 is the same as bar1");
+ if (bar2.getParent() != foo1) {
+ throw new RuntimeException("Parent logger of bar2 "+bar2+" is not foo1 "+foo1);
+ }
+ System.out.println("bar2.getParent() is the same as foo1");
+ final Logger foo2 = Logger.getLogger("foo");
+ if (foo1 != foo2) {
+ throw new RuntimeException("foo2 "+foo2+" is not the same as foo1 "+foo1);
+ }
+ System.out.println("foo2 is the same as foo1");
+
+ System.out.println("Test passed.");
+ }
+}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestScenario.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestScenario.java Fri Sep 06 12:10:30 2013 -0400
@@ -40,7 +40,7 @@
@SuppressWarnings({"rawtypes", "unchecked"})
public enum DoubleStreamTestScenario implements OpTestCase.BaseStreamTestScenario {
- STREAM_FOR_EACH(false) {
+ STREAM_FOR_EACH_WITH_CLOSE(false) {
<T, S_IN extends BaseStream<T, S_IN>>
void _run(TestData<T, S_IN> data, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
DoubleStream s = m.apply(data.stream());
@@ -48,6 +48,7 @@
s = s.sequential();
}
s.forEach(b);
+ s.close();
}
},
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/IntStreamTestScenario.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/stream/bootlib/java/util/stream/IntStreamTestScenario.java Fri Sep 06 12:10:30 2013 -0400
@@ -40,7 +40,7 @@
@SuppressWarnings({"rawtypes", "unchecked"})
public enum IntStreamTestScenario implements OpTestCase.BaseStreamTestScenario {
- STREAM_FOR_EACH(false) {
+ STREAM_FOR_EACH_WITH_CLOSE(false) {
<T, S_IN extends BaseStream<T, S_IN>>
void _run(TestData<T, S_IN> data, IntConsumer b, Function<S_IN, IntStream> m) {
IntStream s = m.apply(data.stream());
@@ -48,6 +48,7 @@
s = s.sequential();
}
s.forEach(b);
+ s.close();
}
},
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/LongStreamTestScenario.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/stream/bootlib/java/util/stream/LongStreamTestScenario.java Fri Sep 06 12:10:30 2013 -0400
@@ -40,7 +40,7 @@
@SuppressWarnings({"rawtypes", "unchecked"})
public enum LongStreamTestScenario implements OpTestCase.BaseStreamTestScenario {
- STREAM_FOR_EACH(false) {
+ STREAM_FOR_EACH_WITH_CLOSE(false) {
<T, S_IN extends BaseStream<T, S_IN>>
void _run(TestData<T, S_IN> data, LongConsumer b, Function<S_IN, LongStream> m) {
LongStream s = m.apply(data.stream());
@@ -48,6 +48,7 @@
s = s.sequential();
}
s.forEach(b);
+ s.close();
}
},
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/StreamTestScenario.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/stream/bootlib/java/util/stream/StreamTestScenario.java Fri Sep 06 12:10:30 2013 -0400
@@ -39,7 +39,7 @@
@SuppressWarnings({"rawtypes", "unchecked"})
public enum StreamTestScenario implements OpTestCase.BaseStreamTestScenario {
- STREAM_FOR_EACH(false) {
+ STREAM_FOR_EACH_WITH_CLOSE(false) {
<T, U, S_IN extends BaseStream<T, S_IN>>
void _run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, Stream<U>> m) {
Stream<U> s = m.apply(data.stream());
@@ -47,6 +47,7 @@
s = s.sequential();
}
s.forEach(b);
+ s.close();
}
},
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/StreamCloseTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+package org.openjdk.tests.java.util.stream;
+
+import java.util.Arrays;
+import java.util.stream.OpTestCase;
+import java.util.stream.Stream;
+
+import org.testng.annotations.Test;
+
+import static java.util.stream.LambdaTestHelpers.countTo;
+
+/**
+ * StreamCloseTest
+ *
+ * @author Brian Goetz
+ */
+@Test(groups = { "serialization-hostile" })
+public class StreamCloseTest extends OpTestCase {
+ public void testEmptyCloseHandler() {
+ try (Stream<Integer> ints = countTo(100).stream()) {
+ ints.forEach(i -> {});
+ }
+ }
+
+ public void testOneCloseHandler() {
+ final boolean[] holder = new boolean[1];
+ Runnable closer = () -> { holder[0] = true; };
+
+ try (Stream<Integer> ints = countTo(100).stream()) {
+ ints.onClose(closer);
+ ints.forEach(i -> {});
+ }
+ assertTrue(holder[0]);
+
+ Arrays.fill(holder, false);
+ try (Stream<Integer> ints = countTo(100).stream().onClose(closer)) {
+ ints.forEach(i -> {});
+ }
+ assertTrue(holder[0]);
+
+ Arrays.fill(holder, false);
+ try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(closer)) {
+ ints.forEach(i -> {});
+ }
+ assertTrue(holder[0]);
+
+ Arrays.fill(holder, false);
+ try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(closer).filter(e -> true)) {
+ ints.forEach(i -> {});
+ }
+ assertTrue(holder[0]);
+ }
+
+ public void testTwoCloseHandlers() {
+ final boolean[] holder = new boolean[2];
+ Runnable close1 = () -> { holder[0] = true; };
+ Runnable close2 = () -> { holder[1] = true; };
+
+ try (Stream<Integer> ints = countTo(100).stream()) {
+ ints.onClose(close1).onClose(close2);
+ ints.forEach(i -> {});
+ }
+ assertTrue(holder[0] && holder[1]);
+
+ Arrays.fill(holder, false);
+ try (Stream<Integer> ints = countTo(100).stream().onClose(close1).onClose(close2)) {
+ ints.forEach(i -> {});
+ }
+ assertTrue(holder[0] && holder[1]);
+
+ Arrays.fill(holder, false);
+ try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2)) {
+ ints.forEach(i -> {});
+ }
+ assertTrue(holder[0] && holder[1]);
+
+ Arrays.fill(holder, false);
+ try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2).filter(e -> true)) {
+ ints.forEach(i -> {});
+ }
+ assertTrue(holder[0] && holder[1]);
+ }
+
+ public void testCascadedExceptions() {
+ final boolean[] holder = new boolean[3];
+ boolean caught = false;
+ Runnable close1 = () -> { holder[0] = true; throw new RuntimeException("1"); };
+ Runnable close2 = () -> { holder[1] = true; throw new RuntimeException("2"); };
+ Runnable close3 = () -> { holder[2] = true; throw new RuntimeException("3"); };
+
+ try (Stream<Integer> ints = countTo(100).stream()) {
+ ints.onClose(close1).onClose(close2).onClose(close3);
+ ints.forEach(i -> {});
+ }
+ catch (RuntimeException e) {
+ assertCascaded(e, 3);
+ assertTrue(holder[0] && holder[1] && holder[2]);
+ caught = true;
+ }
+ assertTrue(caught);
+
+ Arrays.fill(holder, false);
+ caught = false;
+ try (Stream<Integer> ints = countTo(100).stream().onClose(close1).onClose(close2).onClose(close3)) {
+ ints.forEach(i -> {});
+ }
+ catch (RuntimeException e) {
+ assertCascaded(e, 3);
+ assertTrue(holder[0] && holder[1] && holder[2]);
+ caught = true;
+ }
+ assertTrue(caught);
+
+ caught = false;
+ Arrays.fill(holder, false);
+ try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2).onClose(close3)) {
+ ints.forEach(i -> {});
+ }
+ catch (RuntimeException e) {
+ assertCascaded(e, 3);
+ assertTrue(holder[0] && holder[1] && holder[2]);
+ caught = true;
+ }
+ assertTrue(caught);
+
+ caught = false;
+ Arrays.fill(holder, false);
+ try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2).filter(e -> true).onClose(close3)) {
+ ints.forEach(i -> {});
+ }
+ catch (RuntimeException e) {
+ assertCascaded(e, 3);
+ assertTrue(holder[0] && holder[1] && holder[2]);
+ caught = true;
+ }
+ assertTrue(caught);
+ }
+
+ private void assertCascaded(RuntimeException e, int n) {
+ assertTrue(e.getMessage().equals("1"));
+ assertTrue(e.getSuppressed().length == n - 1);
+ for (int i=0; i<n-1; i++)
+ assertTrue(e.getSuppressed()[i].getMessage().equals(String.valueOf(i + 2)));
+ }
+}
--- a/jdk/test/java/util/zip/TestExtraTime.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/java/util/zip/TestExtraTime.java Fri Sep 06 12:10:30 2013 -0400
@@ -23,7 +23,7 @@
/**
* @test
- * @bug 4759491 6303183 7012868 8015666
+ * @bug 4759491 6303183 7012868 8015666 8023713
* @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.util.Arrays;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
@@ -52,24 +53,26 @@
FileTime ctime = FileTime.from(time - 300000, TimeUnit.MILLISECONDS);
TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
- test(mtime, null, null, null);
- // ms-dos 1980 epoch problem
- test(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null);
- // non-default tz
- test(mtime, null, null, tz);
+ for (byte[] extra : new byte[][] { null, new byte[] {1, 2, 3}}) {
+ test(mtime, null, null, null, extra);
+ // ms-dos 1980 epoch problem
+ test(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null, extra);
+ // non-default tz
+ test(mtime, null, null, tz, extra);
- test(mtime, atime, null, null);
- test(mtime, null, ctime, null);
- test(mtime, atime, ctime, null);
+ test(mtime, atime, null, null, extra);
+ test(mtime, null, ctime, null, extra);
+ test(mtime, atime, ctime, null, extra);
- test(mtime, atime, null, tz);
- test(mtime, null, ctime, tz);
- test(mtime, atime, ctime, tz);
+ test(mtime, atime, null, tz, extra);
+ test(mtime, null, ctime, tz, extra);
+ test(mtime, atime, ctime, tz, extra);
+ }
}
}
static void test(FileTime mtime, FileTime atime, FileTime ctime,
- TimeZone tz) throws Throwable {
+ TimeZone tz, byte[] extra) throws Throwable {
System.out.printf("--------------------%nTesting: [%s]/[%s]/[%s]%n",
mtime, atime, ctime);
TimeZone tz0 = TimeZone.getDefault();
@@ -78,8 +81,8 @@
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
- ZipEntry ze = new ZipEntry("TestExtreTime.java");
-
+ ZipEntry ze = new ZipEntry("TestExtraTime.java");
+ ze.setExtra(extra);
ze.setLastModifiedTime(mtime);
if (atime != null)
ze.setLastAccessTime(atime);
@@ -87,6 +90,14 @@
ze.setCreationTime(ctime);
zos.putNextEntry(ze);
zos.write(new byte[] { 1,2 ,3, 4});
+
+ // append an extra entry to help check if the length and data
+ // of the extra field are being correctly written (in previous
+ // entry).
+ if (extra != null) {
+ ze = new ZipEntry("TestExtraEntry");
+ zos.putNextEntry(ze);
+ }
zos.close();
if (tz != null) {
TimeZone.setDefault(tz0);
@@ -96,23 +107,23 @@
new ByteArrayInputStream(baos.toByteArray()));
ze = zis.getNextEntry();
zis.close();
- check(mtime, atime, ctime, ze);
+ check(mtime, atime, ctime, ze, extra);
// ZipFile
Path zpath = Paths.get(System.getProperty("test.dir", "."),
- "TestExtraTimp.zip");
+ "TestExtraTime.zip");
Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath);
ZipFile zf = new ZipFile(zpath.toFile());
- ze = zf.getEntry("TestExtreTime.java");
+ ze = zf.getEntry("TestExtraTime.java");
// ZipFile read entry from cen, which does not have a/ctime,
// for now.
- check(mtime, null, null, ze);
+ check(mtime, null, null, ze, extra);
zf.close();
Files.delete(zpath);
}
static void check(FileTime mtime, FileTime atime, FileTime ctime,
- ZipEntry ze) {
+ ZipEntry ze, byte[] extra) {
/*
System.out.printf(" mtime [%tc]: [%tc]/[%tc]%n",
mtime.to(TimeUnit.MILLISECONDS),
@@ -130,5 +141,17 @@
ctime.to(TimeUnit.SECONDS) !=
ze.getCreationTime().to(TimeUnit.SECONDS))
throw new RuntimeException("Timestamp: storing ctime failed!");
+ if (extra != null) {
+ // if extra data exists, the current implementation put it at
+ // the end of the extra data array (implementation detail)
+ byte[] extra1 = ze.getExtra();
+ if (extra1 == null || extra1.length < extra.length ||
+ !Arrays.equals(Arrays.copyOfRange(extra1,
+ extra1.length - extra.length,
+ extra1.length),
+ extra)) {
+ throw new RuntimeException("Timestamp: storing extra field failed!");
+ }
+ }
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/remote/mandatory/connection/RMIConnectorInternalMapTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+import java.lang.management.ManagementFactory;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.Map;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXPrincipal;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnector;
+import javax.security.auth.Subject;
+
+/*
+ * @test
+ * @bug 6566891
+ * @summary Check no memory leak on RMIConnector's rmbscMap
+ * @author Shanliang JIANG
+ * @run clean RMIConnectorInternalMapTest
+ * @run build RMIConnectorInternalMapTest
+ * @run main RMIConnectorInternalMapTest
+ */
+
+public class RMIConnectorInternalMapTest {
+ public static void main(String[] args) throws Exception {
+ System.out.println("---RMIConnectorInternalMapTest starting...");
+
+ JMXConnectorServer connectorServer = null;
+ JMXConnector connectorClient = null;
+
+ try {
+ MBeanServer mserver = ManagementFactory.getPlatformMBeanServer();
+ JMXServiceURL serverURL = new JMXServiceURL("rmi", "localhost", 0);
+ connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(serverURL, null, mserver);
+ connectorServer.start();
+
+ JMXServiceURL serverAddr = connectorServer.getAddress();
+ connectorClient = JMXConnectorFactory.connect(serverAddr, null);
+ connectorClient.connect();
+
+ Field rmbscMapField = RMIConnector.class.getDeclaredField("rmbscMap");
+ rmbscMapField.setAccessible(true);
+ Map<Subject, WeakReference<MBeanServerConnection>> map =
+ (Map<Subject, WeakReference<MBeanServerConnection>>) rmbscMapField.get(connectorClient);
+ if (map != null && !map.isEmpty()) { // failed
+ throw new RuntimeException("RMIConnector's rmbscMap must be empty at the initial time.");
+ }
+
+ Subject delegationSubject =
+ new Subject(true,
+ Collections.singleton(new JMXPrincipal("delegate")),
+ Collections.EMPTY_SET,
+ Collections.EMPTY_SET);
+ MBeanServerConnection mbsc1 =
+ connectorClient.getMBeanServerConnection(delegationSubject);
+ MBeanServerConnection mbsc2 =
+ connectorClient.getMBeanServerConnection(delegationSubject);
+
+ if (mbsc1 == null) {
+ throw new RuntimeException("Got null connection.");
+ }
+ if (mbsc1 != mbsc2) {
+ throw new RuntimeException("Not got same connection with a same subject.");
+ }
+
+ map = (Map<Subject, WeakReference<MBeanServerConnection>>) rmbscMapField.get(connectorClient);
+ if (map == null || map.isEmpty()) { // failed
+ throw new RuntimeException("RMIConnector's rmbscMap has wrong size "
+ + "after creating a delegated connection.");
+ }
+
+ delegationSubject = null;
+ mbsc1 = null;
+ mbsc2 = null;
+
+ int i = 0;
+ while (!map.isEmpty() && i++ < 60) {
+ System.gc();
+ Thread.sleep(100);
+ }
+ System.out.println("---GC times: " + i);
+
+ if (!map.isEmpty()) {
+ throw new RuntimeException("Failed to clean RMIConnector's rmbscMap");
+ } else {
+ System.out.println("---RMIConnectorInternalMapTest: PASSED!");
+ }
+ } finally {
+ try {
+ connectorClient.close();
+ connectorServer.stop();
+ } catch (Exception e) {
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/remote/mandatory/connection/RMIConnectorNullSubjectConnTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+import java.lang.management.ManagementFactory;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnector;
+
+/*
+ * @test
+ * @bug 6566891
+ * @summary Check no memory leak on RMIConnector's nullSubjectConn
+ * @author Shanliang JIANG
+ * @run clean RMIConnectorNullSubjectConnTest
+ * @run build RMIConnectorNullSubjectConnTest
+ * @run main RMIConnectorNullSubjectConnTest
+ */
+
+public class RMIConnectorNullSubjectConnTest {
+ public static void main(String[] args) throws Exception {
+ System.out.println("---RMIConnectorNullSubjectConnTest starting...");
+
+ JMXConnectorServer connectorServer = null;
+ JMXConnector connectorClient = null;
+
+ try {
+ MBeanServer mserver = ManagementFactory.getPlatformMBeanServer();
+ JMXServiceURL serverURL = new JMXServiceURL("rmi", "localhost", 0);
+ connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(serverURL, null, mserver);
+ connectorServer.start();
+
+ JMXServiceURL serverAddr = connectorServer.getAddress();
+ connectorClient = JMXConnectorFactory.connect(serverAddr, null);
+ connectorClient.connect();
+
+ Field nullSubjectConnField = RMIConnector.class.getDeclaredField("nullSubjectConnRef");
+ nullSubjectConnField.setAccessible(true);
+
+ WeakReference<MBeanServerConnection> weak =
+ (WeakReference<MBeanServerConnection>)nullSubjectConnField.get(connectorClient);
+
+ if (weak != null && weak.get() != null) {
+ throw new RuntimeException("nullSubjectConnRef must be null at initial time.");
+ }
+
+ MBeanServerConnection conn1 = connectorClient.getMBeanServerConnection(null);
+ MBeanServerConnection conn2 = connectorClient.getMBeanServerConnection(null);
+ if (conn1 == null) {
+ throw new RuntimeException("A connection with null subject should not be null.");
+ } else if (conn1 != conn2) {
+ throw new RuntimeException("The 2 connections with null subject are not equal.");
+ }
+
+ conn1 = null;
+ conn2 = null;
+ int i = 1;
+ do {
+ System.gc();
+ Thread.sleep(100);
+ weak = (WeakReference<MBeanServerConnection>)nullSubjectConnField.get(connectorClient);
+ } while ((weak != null && weak.get() != null) && i++ < 60);
+
+ System.out.println("---GC times: " + i);
+
+ if (weak != null && weak.get() != null) {
+ throw new RuntimeException("Failed to clean RMIConnector's nullSubjectConn");
+ } else {
+ System.out.println("---RMIConnectorNullSubjectConnTest: PASSED!");
+ }
+ } finally {
+ try {
+ connectorClient.close();
+ connectorServer.stop();
+ } catch (Exception e) {
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/jdk/lambda/MethodReferenceTestCallerSensitive.java Fri Sep 06 12:10:30 2013 -0400
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Field;
+import java.util.function.Function;
+
+
+/**
+ * @author Robert Field
+ */
+
+@Test
+public class MethodReferenceTestCallerSensitive {
+
+ private static <T> void getF(T arg) {
+ Function<Class<T>,Field[]> firstFunction = Class<T>::getFields;
+ }
+
+ public void testConstructorReferenceVarArgs() {
+ getF("Hello World");
+ }
+
+}
--- a/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/IllegalSNIName.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/sun/security/ssl/javax/net/ssl/ServerName/IllegalSNIName.java Fri Sep 06 12:10:30 2013 -0400
@@ -34,7 +34,7 @@
public static void main(String[] args) throws Exception {
String[] illegalNames = {
- "example\u3003\u3002com",
+ "example\u3002\u3002com",
"example..com",
"com\u3002",
"com.",
--- a/jdk/test/sun/tools/jconsole/ImmutableResourceTest.java Fri Sep 06 12:04:18 2013 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved.
- * 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 isn't the test case: ImmutableResourceTest.sh is.
- * Refer to ImmutableResourceTest.sh when running this test.
- *
- * @bug 6287579
- * @summary SubClasses of ListResourceBundle should fix getContents()
- */
-import java.util.ResourceBundle;
-
-public class ImmutableResourceTest {
-
- public static void main(String[] args) throws Exception {
-
- /* Reach under the covers and get the message strings */
- sun.tools.jconsole.resources.JConsoleResources jcr =
- new sun.tools.jconsole.resources.JConsoleResources ();
- Object [][] testData = jcr.getContents();
-
- /* Shred our copy of the message strings */
- for (int ii = 0; ii < testData.length; ii++) {
- testData[ii][0] = "xxx";
- testData[ii][1] = "yyy";
- }
-
- /*
- * Try a lookup for the shredded key.
- * If this is successful we have a problem.
- */
- String ss = sun.tools.jconsole.Resources.getText("xxx");
- if ("yyy".equals(ss)) {
- throw new Exception ("SubClasses of ListResourceBundle should fix getContents()");
- }
- System.out.println("...Finished.");
- }
-}
--- a/jdk/test/sun/tools/jconsole/ImmutableResourceTest.sh Fri Sep 06 12:04:18 2013 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-#
-# Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
-# 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 6287579
-# @summary SubClasses of ListResourceBundle should fix getContents()
-#
-# @run shell ImmutableResourceTest.sh
-
-# Beginning of subroutines:
-status=1
-
-#Call this from anywhere to fail the test with an error message
-# usage: fail "reason why the test failed"
-fail()
- { echo "The test failed :-("
- echo "$*" 1>&2
- echo "exit status was $status"
- exit $status
- } #end of fail()
-
-#Call this from anywhere to pass the test with a message
-# usage: pass "reason why the test passed if applicable"
-pass()
- { echo "The test passed!!!"
- echo "$*" 1>&2
- exit 0
- } #end of pass()
-
-# end of subroutines
-
-# The beginning of the script proper
-
-OS=`uname -s`
-case "$OS" in
- SunOS | Linux | Darwin )
- PATHSEP=":"
- ;;
-
- Windows* | CYGWIN*)
- PATHSEP=";"
- ;;
-
- # catch all other OSs
- * )
- echo "Unrecognized system! $OS"
- fail "Unrecognized system! $OS"
- ;;
-esac
-
-TARGETCLASS="ImmutableResourceTest"
-if [ -z "${TESTJAVA}" ] ; then
- # TESTJAVA is not set, so the test is running stand-alone.
- # TESTJAVA holds the path to the root directory of the build of the JDK
- # to be tested. That is, any java files run explicitly in this shell
- # should use TESTJAVA in the path to the java interpreter.
- # So, we'll set this to the JDK spec'd on the command line. If none
- # is given on the command line, tell the user that and use a default.
- # THIS IS THE JDK BEING TESTED.
- if [ -n "$1" ] ; then
- TESTJAVA=$1
- else
- TESTJAVA=$JAVA_HOME
- fi
- TESTSRC=.
- TESTCLASSES=.
- #Deal with .class files:
-fi
-#
-echo "JDK under test is: $TESTJAVA"
-#
-CP="-classpath ${TESTCLASSES}${PATHSEP}${TESTJAVA}/lib/jconsole.jar"
-# Compile the test class using the classpath we need:
-#
-env
-#
-set -vx
-#
-#Compile. jconsole.jar is required on the classpath.
-${TESTJAVA}/bin/javac -d "${TESTCLASSES}" ${CP} -g \
- "${TESTSRC}"/"${TARGETCLASS}".java
-#
-#Run the test class, again with the classpath we need:
-${TESTJAVA}/bin/java ${CP} ${TARGETCLASS}
-status=$?
-echo "test status was: $status"
-if [ $status -eq "0" ];
- then pass ""
-
- else fail "unspecified test failure"
-fi
--- a/jdk/test/sun/tools/jconsole/ResourceCheckTest.java Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/sun/tools/jconsole/ResourceCheckTest.java Fri Sep 06 12:10:30 2013 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -27,377 +27,134 @@
* This isn't the test case: ResourceCheckTest.sh is.
* Refer to ResourceCheckTest.sh when running this test.
*
- * @bug 5008856 5023573 5024917 5062569
+ * @bug 5008856 5023573 5024917 5062569 7172176
* @summary 'missing resource key' error for key = "Operating system"
*/
-import java.awt.event.KeyEvent;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import sun.tools.jconsole.Messages;
import sun.tools.jconsole.Resources;
+/*
+ * Ensures that there is a one-to-one mapping between constants in the
+ * Message class and the keys in the sun.tools.jconsole.resources.messages
+ * bundle.
+ *
+ * An error will be thrown if there is a:
+ *
+ * - key in the resource bundle that doesn't have a public static field with
+ * the same name in the Message class.
+ *
+ * - public static field in the Message class that doesn't have a key with
+ * the same name in the resource bundle.
+ *
+ * - message with a mnemonic identifier(&) for which a mnemonic can't
+ * be looked up using Resources#getMnemonicInt().
+ *
+ */
public class ResourceCheckTest {
+ private static final String MISSING_RESOURCE_KEY_PREFIX = "missing message for";
+ private static final String RESOURCE_BUNDLE = "sun.tools.jconsole.resources.messages";
+ private static final String NEW_LINE = String.format("%n");
- public static void main(String[] args){
- Object [][] testData = {
- {"<", "", "", "", ""},
- {"<<", "", "", "", ""},
- {">", "", "", "", ""},
- {" 1 day", "", "", "", ""},
- {" 1 hour", "", "", "", ""},
- {" 1 min", "", "", "", ""},
- {" 1 month", "", "", "", ""},
- {" 1 year", "", "", "", ""},
- {" 2 hours", "", "", "", ""},
- {" 3 hours", "", "", "", ""},
- {" 3 months", "", "", "", ""},
- {" 5 min", "", "", "", ""},
- {" 6 hours", "", "", "", ""},
- {" 6 months", "", "", "", ""},
- {" 7 days", "", "", "", ""},
- {"10 min", "", "", "", ""},
- {"12 hours", "", "", "", ""},
- {"30 min", "", "", "", ""},
- {"ACTION", "", "", "", ""},
- {"ACTION_INFO", "", "", "", ""},
- {"All", "", "", "", ""},
- {"Architecture", "", "", "", ""},
- {"Attribute", "", "", "", ""},
- {"Attribute value", "", "", "", ""},
- {"Attribute values", "", "", "", ""},
- {"Attributes", "", "", "", ""},
- {"Blank", "", "", "", ""},
- {"BlockedCount WaitedCount", "BlockedCount", "WaitedCount", "", ""},
- {"Boot class path", "", "", "", ""},
- {"BorderedComponent.moreOrLessButton.toolTip", "", "", "", ""},
- {"Close", "", "", "", ""},
- {"CPU Usage", "", "", "", ""},
- {"CPUUsageFormat","PhonyPercentage", "", "", ""},
- {"Cancel", "", "", "", ""},
- {"Cascade", "", "", "", ""},
- {"Cascade.mnemonic", "", "", "", ""},
- {"Chart:", "", "", "", ""},
- {"Chart:.mnemonic", "", "", "", ""},
- {"ClassTab.infoLabelFormat", "LoadedCount", "UnloadedCount", "TotalCount", ""},
- {"ClassTab.loadedClassesPlotter.accessibleName", "", "", "", ""},
- {"Class path", "", "", "", ""},
- {"Classes", "", "", "", ""},
- {"ClassName", "", "", "", ""},
- {"Column.Name", "", "", "", ""},
- {"Column.PID", "", "", "", ""},
- {"Committed", "", "", "", ""},
- {"Committed memory", "", "", "", ""},
- {"Committed virtual memory", "", "", "", ""},
- {"Compiler", "", "", "", ""},
- {"Connect...", "", "", "", ""},
- {"Connect", "", "", "", ""},
- {"Connect.mnemonic", "", "", "", ""},
- {"ConnectDialog.connectButton.toolTip", "", "", "", ""},
- {"ConnectDialog.accessibleDescription", "", "", "", ""},
- {"ConnectDialog.masthead.accessibleName", "", "", "", ""},
- {"ConnectDialog.masthead.title", "", "", "", ""},
- {"ConnectDialog.statusBar.accessibleName", "", "", "", ""},
- {"ConnectDialog.title", "", "", "", ""},
- {"Connected. Click to disconnect.", "", "", "", ""},
- {"connectingTo1", "PhonyConnectionName", "", "", ""},
- {"connectingTo2", "PhonyConnectionName", "", "", ""},
- {"connectionFailed1", "", "", "", ""},
- {"connectionFailed2", "PhonyConnectionName", "", "", ""},
- {"connectionLost1", "", "", "", ""},
- {"connectionLost2", "PhonyConnectionName", "", "", ""},
- {"Connection failed", "", "", "", ""},
- {"Connection", "", "", "", ""},
- {"Connection.mnemonic", "", "", "", ""},
- {"Connection name", "", "", "", ""},
- {"ConnectionName (disconnected)", "Phony", "Phony", "", ""},
- {"Constructor", "", "", "", ""},
- {"Create", "Phony", "Phony", "", ""},
- {"Current classes loaded", "", "", "", ""},
- {"Current heap size", "", "", "", ""},
- {"Current value", "PhonyValue", "", "", ""},
- {"Daemon threads", "", "", "", ""},
- {"deadlockAllTab", "", "", "", ""},
- {"deadlockTab", "", "", "", ""},
- {"deadlockTabN", "PhonyInt", "", "", ""},
- {"Description", "", "", "", ""},
- {"Descriptor", "", "", "", ""},
- {"Details", "", "", "", ""},
- {"Detect Deadlock", "", "", "", ""},
- {"Detect Deadlock.mnemonic", "", "", "", ""},
- {"Detect Deadlock.toolTip", "", "", "", ""},
- {"Dimension is not supported:", "", "", "", ""},
- {"Discard chart", "", "", "", ""},
- {"Disconnected. Click to connect.", "", "", "", ""},
- {"Double click to expand/collapse", "", "", "", ""},
- {"Double click to visualize", "", "", "", ""},
- {"DurationDaysHoursMinutes", 0, 13, 54, ""},
- {"DurationDaysHoursMinutes", 1, 13, 54, ""},
- {"DurationDaysHoursMinutes", 2, 13, 54, ""},
- {"DurationDaysHoursMinutes", 1024, 13, 45, ""},
- {"DurationHoursMinutes", 0, 13, "", ""},
- {"DurationHoursMinutes", 1, 0, "", ""},
- {"DurationHoursMinutes", 1, 1, "", ""},
- {"DurationHoursMinutes", 2, 42, "", ""},
- {"DurationMinutes", 0, "", "", ""},
- {"DurationMinutes", 1, "", "", ""},
- {"DurationMinutes", 2, "", "", ""},
- {"DurationSeconds", 0, "", "", ""},
- {"DurationSeconds", 1, "", "", ""},
- {"DurationSeconds", 2, "", "", ""},
- {"Empty array", "", "", "", ""},
- {"Error", "", "", "", ""},
- {"Error: MBeans already exist", "", "", "", ""},
- {"Error: MBeans do not exist", "", "", "", ""},
- {"Event", "", "", "", ""},
- {"Exit", "", "", "", ""},
- {"Exit.mnemonic", "", "", "", ""},
- {"expand", "", "", "", ""},
- {"Fail to load plugin", "", "", "", ""},
- {"FileChooser.fileExists.cancelOption", "", "", "", ""},
- {"FileChooser.fileExists.message", "PhonyFileName", "", "", ""},
- {"FileChooser.fileExists.okOption", "", "", "", ""},
- {"FileChooser.fileExists.title", "", "", "", ""},
- {"FileChooser.savedFile", "PhonyFilePath", "PhonyFileSize", "", ""},
- {"FileChooser.saveFailed.message", "PhonyFilePath", "PhonyMessage", "", ""},
- {"FileChooser.saveFailed.title", "", "", "", ""},
- {"Free physical memory", "", "", "", ""},
- {"Free swap space", "", "", "", ""},
- {"Garbage collector", "", "", "", ""},
- {"GC time", "", "", "", ""},
- {"GC time details", 54, "Phony", 11, ""},
- {"GcInfo", "Phony", -1, 768, ""},
- {"GcInfo", "Phony", 0, 768, ""},
- {"GcInfo", "Phony", 1, 768, ""},
- {"Heap", "", "", "", ""},
- {"Heap Memory Usage", "", "", "", ""},
- {"Help.AboutDialog.accessibleDescription", "", "", "", ""},
- {"Help.AboutDialog.jConsoleVersion", "DummyVersion", "", "", ""},
- {"Help.AboutDialog.javaVersion", "DummyVersion", "", "", ""},
- {"Help.AboutDialog.masthead.accessibleName", "", "", "", ""},
- {"Help.AboutDialog.masthead.title", "", "", "", ""},
- {"Help.AboutDialog.title", "", "", "", ""},
- {"Help.AboutDialog.userGuideLink", "DummyMessage", "", "", ""},
- {"Help.AboutDialog.userGuideLink.mnemonic", "", "", "", ""},
- {"Help.AboutDialog.userGuideLink.url", "DummyURL", "", "", ""},
- {"HelpMenu.About.title", "", "", "", ""},
- {"HelpMenu.About.title.mnemonic", "", "", "", ""},
- {"HelpMenu.UserGuide.title", "", "", "", ""},
- {"HelpMenu.UserGuide.title.mnemonic", "", "", "", ""},
- {"HelpMenu.title", "", "", "", ""},
- {"HelpMenu.title.mnemonic", "", "", "", ""},
- {"Hotspot MBeans...", "", "", "", ""},
- {"Hotspot MBeans....mnemonic", "", "", "", ""},
- {"Hotspot MBeans.dialog.accessibleDescription", "", "", "", ""},
- {"Impact", "", "", "", ""},
- {"Info", "", "", "", ""},
- {"INFO", "", "", "", ""},
- {"Invalid plugin path", "", "", "", ""},
- {"Invalid URL", "", "", "", ""},
- {"Is", "", "", "", ""},
- {"Java Monitoring & Management Console", "", "", "", ""},
- {"Java Virtual Machine", "", "", "", ""},
- {"JConsole: ", "", "", "", ""},
- {"JConsole.accessibleDescription", "", "", "", ""},
- {"JConsole version", "PhonyVersion", "", "", ""},
- {"JIT compiler", "", "", "", ""},
- {"Library path", "", "", "", ""},
- {"Live Threads", "", "", "", ""},
- {"Loaded", "", "", "", ""},
- {"Local Process:", "", "", "", ""},
- {"Local Process:.mnemonic", "", "", "", ""},
- {"Manage Hotspot MBeans in: ", "", "", "", ""},
- {"Management Not Enabled", "", "", "", ""},
- {"Management Will Be Enabled", "", "", "", ""},
- {"Masthead.font", "", "", "", ""},
- {"Max", "", "", "", ""},
- {"Max", "", "", "", ""},
- {"Maximum heap size", "", "", "", ""},
- {"MBeanAttributeInfo", "", "", "", ""},
- {"MBeanInfo", "", "", "", ""},
- {"MBeanNotificationInfo", "", "", "", ""},
- {"MBeanOperationInfo", "", "", "", ""},
- {"MBeans", "", "", "", ""},
- {"MBeansTab.clearNotificationsButton", "", "", "", ""},
- {"MBeansTab.clearNotificationsButton.mnemonic", "", "", "", ""},
- {"MBeansTab.clearNotificationsButton.toolTip", "", "", "", ""},
- {"MBeansTab.compositeNavigationMultiple", 0, 0, "", ""},
- {"MBeansTab.compositeNavigationSingle", "", "", "", ""},
- {"MBeansTab.refreshAttributesButton", "", "", "", ""},
- {"MBeansTab.refreshAttributesButton.mnemonic", "", "", "", ""},
- {"MBeansTab.refreshAttributesButton.toolTip", "", "", "", ""},
- {"MBeansTab.subscribeNotificationsButton", "", "", "", ""},
- {"MBeansTab.subscribeNotificationsButton.mnemonic", "", "", "", ""},
- {"MBeansTab.subscribeNotificationsButton.toolTip", "", "", "", ""},
- {"MBeansTab.tabularNavigationMultiple", 0, 0, "", ""},
- {"MBeansTab.tabularNavigationSingle", "", "", "", ""},
- {"MBeansTab.unsubscribeNotificationsButton", "", "", "", ""},
- {"MBeansTab.unsubscribeNotificationsButton.mnemonic", "", "", "", ""},
- {"MBeansTab.unsubscribeNotificationsButton.toolTip", "", "", "", ""},
- {"Memory", "", "", "", ""},
- {"MemoryPoolLabel", "PhonyMemoryPool", "", "", ""},
- {"MemoryTab.heapPlotter.accessibleName", "", "", "", ""},
- {"MemoryTab.infoLabelFormat", "UsedCount", "CommittedCount", "MaxCount", ""},
- {"MemoryTab.nonHeapPlotter.accessibleName", "", "", "", ""},
- {"MemoryTab.poolChart.aboveThreshold", "Threshold", "", "", ""},
- {"MemoryTab.poolChart.accessibleName", "", "", "", ""},
- {"MemoryTab.poolChart.belowThreshold", "Threshold", "", "", ""},
- {"MemoryTab.poolPlotter.accessibleName", "PhonyMemoryPool", "", "", ""},
- {"Message", "", "", "", ""},
- {"Method successfully invoked", "", "", "", ""},
- {"Monitor locked", "", "", "", ""},
- {"Minimize All", "", "", "", ""},
- {"Minimize All.mnemonic", "", "", "", ""},
- {"Name", "", "", "", ""},
- {"Name and Build", "PhonyName", "PhonyBuild", "", ""},
- {"Name Build and Mode", "PhonyName", "PhonyBuild", "PhonyMode", ""},
- {"Name State", "PhonyName", "PhonyState", "", ""},
- {"Name State LockName", "PhonyName", "PhonyState", "PhonyLock", ""},
- {"Name State LockName LockOwner", "PhonyName", "PhonyState", "PhonyLock", "PhonyOwner"},
- {"New Connection...", "", "", "", ""},
- {"New Connection....mnemonic", "", "", "", ""},
- {"No deadlock detected", "", "", "", ""},
- {"Non-Heap", "", "", "", ""},
- {"Non-Heap Memory Usage", "", "", "", ""},
- {"Notification", "", "", "", ""},
- {"Notification buffer", "", "", "", ""},
- {"Notifications", "", "", "", ""},
- {"NotifTypes", "", "", "", ""},
- {"Number of Loaded Classes", "", "", "", ""},
- {"Number of processors", "", "", "", ""},
- {"Number of Threads", "", "", "", ""},
- {"ObjectName", "", "", "", ""},
- {"Operating System", "", "", "", ""},
- {"Operation", "", "", "", ""},
- {"Operation invocation", "", "", "", ""},
- {"Operation return value", "", "", "", ""},
- {"Operations", "", "", "", ""},
- {"Overview", "", "", "", ""},
- {"OverviewPanel.plotter.accessibleName", "PhonyPlotter", "", "", ""},
- {"Parameter", "", "", "", ""},
- {"Password: ", "", "", "", ""},
- {"Password: .mnemonic", "", "", "", ""},
- {"Password.accessibleName", "", "", "", ""},
- {"Peak", "", "", "", ""},
- {"Perform GC", "", "", "", ""},
- {"Perform GC.mnemonic", "", "", "", ""},
- {"Perform GC.toolTip", "", "", "", ""},
- {"Plotter.accessibleName", "", "", "", ""},
- {"Plotter.accessibleName.keyAndValue", "Key", "Value", "", ""},
- {"Plotter.accessibleName.noData", "", "", "", ""},
- {"Plotter.saveAsMenuItem", "", "", "", ""},
- {"Plotter.saveAsMenuItem.mnemonic", "", "", "", ""},
- {"Plotter.timeRangeMenu", "", "", "", ""},
- {"Plotter.timeRangeMenu.mnemonic", "", "", "", ""},
- {"plot", "", "", "", ""},
- {"Problem adding listener", "", "", "", ""},
- {"Problem displaying MBean", "", "", "", ""},
- {"Problem invoking", "", "", "", ""},
- {"Problem removing listener", "", "", "", ""},
- {"Problem setting attribute", "", "", "", ""},
- {"Process CPU time", "", "", "", ""},
- {"Readable", "", "", "", ""},
- {"Reconnect", "", "", "", ""},
- {"Remote Process:", "", "", "", ""},
- {"Remote Process:.mnemonic", "", "", "", ""},
- {"Remote Process.textField.accessibleName", "", "", "", ""},
- {"remoteTF.usage", "", "", "", ""},
- {"Restore All", "", "", "", ""},
- {"Restore All.mnemonic", "", "", "", ""},
- {"ReturnType", "", "", "", ""},
- {"SeqNum", "", "", "", ""},
- {"Size Bytes", 512, "", "", ""},
- {"Size Gb", 512, "", "", ""},
- {"Size Kb", 512, "", "", ""},
- {"Size Mb", 512, "", "", ""},
- {"Source", "", "", "", ""},
- {"Stack trace", "", "", "", ""},
- {"SummaryTab.headerDateTimeFormat", "", "", "", ""},
- {"SummaryTab.pendingFinalization.label", "", "", "", ""},
- {"SummaryTab.pendingFinalization.value", "ObjectCount", "", "", ""},
- {"SummaryTab.tabName", "", "", "", ""},
- {"SummaryTab.vmVersion", "VMName", "VMVersion", "", ""},
- {"ThreadTab.infoLabelFormat", "LiveCount", "PeakCount", "TotalCount", ""},
- {"ThreadTab.threadInfo.accessibleName", "", "", "", ""},
- {"ThreadTab.threadPlotter.accessibleName", "", "", "", ""},
- {"Threads", "", "", "", ""},
- {"Threshold", "", "", "", ""},
- {"Tile", "", "", "", ""},
- {"Tile.mnemonic", "", "", "", ""},
- {"Time", "", "", "", ""},
- {"Time Range:", "", "", "", ""},
- {"Time Range:.mnemonic", "", "", "", ""},
- {"TimeStamp", "", "", "", ""},
- {"Total classes loaded", "", "", "", ""},
- {"Total classes unloaded", "", "", "", ""},
- {"Total compile time", "", "", "", ""},
- {"Total Loaded", "", "", "", ""},
- {"Total physical memory", "", "", "", ""},
- {"Total swap space", "", "", "", ""},
- {"Total threads started", "", "", "", ""},
- {"Type", "", "", "", ""},
- {"Unavailable", "", "", "", ""},
- {"UNKNOWN", "", "", "", ""},
- {"Unregister", "", "", "", ""},
- {"Uptime", "", "", "", ""},
- {"Usage Threshold", "", "", "", ""},
- {"Used", "", "", "", ""},
- {"Username: ", "", "", "", ""},
- {"Username: .mnemonic", "", "", "", ""},
- {"Username.accessibleName", "", "", "", ""},
- {"UserData", "", "", "", ""},
- {"Value", "", "", "", ""},
- {"Vendor", "", "", "", ""},
- {"Verbose Output", "", "", "", ""},
- {"Verbose Output.toolTip", "", "", "", ""},
- {"visualize", "", "", "", ""},
- {"VM", "", "", "", ""},
- {"VMInternalFrame.accessibleDescription", "", "", "", ""},
- {"VM arguments", "", "", "", ""},
- {"Virtual Machine", "", "", "", ""},
- {"Window", "", "", "", ""},
- {"Window.mnemonic", "", "", "", ""},
- {"Writable", "", "", "", ""},
- {"zz usage text", "PhonyName", "", "", ""},
- };
- //boolean verbose = false;
- boolean verbose = true;
-
- long badLookups = 0;
- System.out.println("Start...");
- for (int ii = 0; ii < testData.length; ii++) {
- String key = (String)testData[ii][0];
-
- if (key.endsWith(".mnemonic")) {
- String baseKey = key.substring(0, key.length() - ".mnemonic".length());
- int mnemonic = Resources.getMnemonicInt(baseKey);
- if (mnemonic == 0) {
- badLookups++;
- System.out.println("****lookup failed for key = " + key);
+ public static void main(String... args) {
+ List<String> errors = new ArrayList<>();
+ // Ensure that all Message fields have a corresponding key/value
+ // in the resource bundle and that mnemonics can be looked
+ // up where applicable.
+ ResourceBundle rb = ResourceBundle.getBundle(RESOURCE_BUNDLE);
+ for (Field field : Messages.class.getFields()) {
+ if (isResourceKeyField(field)) {
+ String resourceKey = field.getName();
+ String message = readField(field);
+ if (message.startsWith(MISSING_RESOURCE_KEY_PREFIX)) {
+ errors.add("Can't find message (and perhaps mnemonic) for "
+ + Messages.class.getSimpleName() + "."
+ + resourceKey + " in resource bundle.");
} else {
- if (verbose) {
- System.out.println(" mnemonic: " + KeyEvent.getKeyText(mnemonic));
+ String resourceMessage = rb.getString(resourceKey);
+ if (hasMnemonicIdentifier(resourceMessage)) {
+ int mi = Resources.getMnemonicInt(message);
+ if (mi == 0) {
+ errors.add("Could not look up mnemonic for message '"
+ + message + "'.");
+ }
}
}
- continue;
- }
-
- String ss = Resources.getText(key,
- testData[ii][1],
- testData[ii][2],
- testData[ii][3],
- testData[ii][4]);
- if (ss.startsWith("missing resource key")) {
- badLookups++;
- System.out.println("****lookup failed for key = " + key);
- } else {
- if (verbose) {
- System.out.println(" " + ss);
- }
}
}
- if (badLookups > 0) {
- throw new Error ("Resource lookup failed " + badLookups +
- " time(s); Test failed");
+
+ // Ensure that there is Message class field for every resource key.
+ for (String key : Collections.list(rb.getKeys())) {
+ try {
+ Messages.class.getField(key);
+ } catch (NoSuchFieldException nfe) {
+ errors.add("Can't find static field ("
+ + Messages.class.getSimpleName() + "." + key
+ + ") matching '" + key
+ + "' in resource bundle. Unused message?");
+ }
+ }
+
+ if (errors.size() > 0) {
+ throwError(errors);
+ }
+ }
+
+ private static String readField(Field field) {
+ try {
+ return (String) field.get(null);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new Error("Could not access field " + field.getName()
+ + " when trying to read resource message.");
}
- System.out.println("...Finished.");
+ }
+
+ private static boolean isResourceKeyField(Field field) {
+ int modifiers = field.getModifiers();
+ return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers);
+ }
+
+ private static boolean hasMnemonicIdentifier(String s) {
+ for (int i = 0; i < s.length() - 1; i++) {
+ if (s.charAt(i) == '&') {
+ if (s.charAt(i + 1) != '&') {
+ return true;
+ } else {
+ i++;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static void throwError(List<String> errors) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("Found ");
+ buffer.append(errors.size());
+ buffer.append(" error(s) when checking one-to-one mapping ");
+ buffer.append("between Message and resource bundle keys in ");
+ buffer.append(RESOURCE_BUNDLE);
+ buffer.append(" with ");
+ buffer.append(Locale.getDefault());
+ buffer.append(" locale.");
+ buffer.append(NEW_LINE);
+ int errorIndex = 1;
+ for (String error : errors) {
+ buffer.append("Error ");
+ buffer.append(errorIndex);
+ buffer.append(": ");
+ buffer.append(error);
+ buffer.append(NEW_LINE);
+ errorIndex++;
+ }
+ throw new Error(buffer.toString());
}
}
--- a/jdk/test/sun/tools/jconsole/ResourceCheckTest.sh Fri Sep 06 12:04:18 2013 -0400
+++ b/jdk/test/sun/tools/jconsole/ResourceCheckTest.sh Fri Sep 06 12:10:30 2013 -0400
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2004, 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
@@ -54,7 +54,7 @@
OS=`uname -s`
case "$OS" in
- SunOS | Linux )
+ SunOS | Linux | Darwin)
PATHSEP=":"
;;