# HG changeset patch # User duke # Date 1499281834 -7200 # Node ID 29595f2d11d812a4bc2ce47328eec2318fee73ef # Parent 9f30c43b7024aef092565ecde8dfaa8351d1ad07# Parent 774940199f53197e495ea8721856db63ff8f6c26 Merge diff -r 9f30c43b7024 -r 29595f2d11d8 .hgtags-top-repo --- a/.hgtags-top-repo Wed Dec 23 15:41:50 2015 -0800 +++ b/.hgtags-top-repo Wed Jul 05 21:10:34 2017 +0200 @@ -341,3 +341,4 @@ 5582a79892596169ebddb3e2c2aa44939e4e3f40 jdk-9+96 75c3897541ecb52ee16d001ea605b12971df7303 jdk-9+97 48987460c7d49a29013963ee44d090194396bb61 jdk-9+98 +7c0577bea4c65d69c5bef67023a89d2efa4fb2f7 jdk-9+99 diff -r 9f30c43b7024 -r 29595f2d11d8 common/bin/jib.sh --- a/common/bin/jib.sh Wed Dec 23 15:41:50 2015 -0800 +++ b/common/bin/jib.sh Wed Jul 05 21:10:34 2017 +0200 @@ -32,7 +32,7 @@ install_data=${mydir}/../../.jib/.data setup_url() { - if [ -f "~/.config/jib/jib.conf" ]; then + if [ -f ~/.config/jib/jib.conf ]; then source ~/.config/jib/jib.conf fi @@ -50,6 +50,9 @@ if [ -n "${JIB_SERVER}" ]; then jib_server="${JIB_SERVER}" fi + if [ -n "${JIB_SERVER_MIRRORS}" ]; then + jib_server_mirrors="${JIB_SERVER_MIRRORS}" + fi if [ -n "${JIB_REPOSITORY}" ]; then jib_repository="${JIB_REPOSITORY}" fi @@ -70,8 +73,9 @@ jib_url="${JIB_URL}" data_string="${jib_url}" else - data_string="${jib_repository}/${jib_organization}/${jib_module}/${jib_revision}/${jib_module}-${jib_revision}.${jib_ext}" - jib_url="${jib_server}/${data_string}" + jib_path="${jib_repository}/${jib_organization}/${jib_module}/${jib_revision}/${jib_module}-${jib_revision}.${jib_ext}" + data_string="${jib_path}" + jib_url="${jib_server}/${jib_path}" fi } @@ -104,7 +108,25 @@ ${getcmd} ${jib_url} > "${installed_jib_script}.gz" if [ ! -s "${installed_jib_script}.gz" ]; then echo "Failed to download ${jib_url}" - exit 1 + if [ -n "${jib_path}" -a -n "${jib_server_mirrors}" ]; then + OLD_IFS="${IFS}" + IFS=" ," + for mirror in ${jib_server_mirrors}; do + echo "Trying mirror ${mirror}" + jib_url="${mirror}/${jib_path}" + ${getcmd} ${jib_url} > "${installed_jib_script}.gz" + if [ -s "${installed_jib_script}.gz" ]; then + echo "Download from mirror successful" + break + else + echo "Failed to download ${jib_url}" + fi + done + IFS="${OLD_IFS}" + fi + if [ ! -s "${installed_jib_script}.gz" ]; then + exit 1 + fi fi echo "Extracting JIB bootstrap script" rm -f "${installed_jib_script}" diff -r 9f30c43b7024 -r 29595f2d11d8 hotspot/.hgtags --- a/hotspot/.hgtags Wed Dec 23 15:41:50 2015 -0800 +++ b/hotspot/.hgtags Wed Jul 05 21:10:34 2017 +0200 @@ -501,3 +501,4 @@ a94bb7203596dd632486f1e3655fa5f70541dc08 jdk-9+96 de592ea5f7ba0f8a8c5afc03bd169f7690c72b6f jdk-9+97 e5b1a23be1e105417ba1c4c576ab373eb3fa2c2b jdk-9+98 +f008e8cc10d5b3212fb22d58c96fa01d38654f19 jdk-9+99 diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/.hgtags --- a/jdk/.hgtags Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/.hgtags Wed Jul 05 21:10:34 2017 +0200 @@ -341,3 +341,4 @@ c021b855f51e572e63982654b17742cb1f814fb4 jdk-9+96 fdd84b2265ddce7f50e084b7c8635189bba6f012 jdk-9+97 f86ee68d1107dad41a27efc34306e0e56244a12e jdk-9+98 +e1a789be1535741274c9779f4d4ca3495196b5c3 jdk-9+99 diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/make/launcher/Launcher-jdk.accessibility.gmk --- a/jdk/make/launcher/Launcher-jdk.accessibility.gmk Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/make/launcher/Launcher-jdk.accessibility.gmk Wed Jul 05 21:10:34 2017 +0200 @@ -73,8 +73,9 @@ $$(eval $$(call SetupNativeCompilation, BUILD_JACCESSINSPECTOR$1, \ SRC := $(TOPDIR)/jaccessinspector $(TOPDIR)/common \ $(TOPDIR)/toolscommon $(TOPDIR)/include/bridge, \ - CFLAGS := $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 /EHsc, \ - LDFLAGS := $$(LDFLAGS_JDKEXE) /STACK:655360 Advapi32.lib User32.lib, \ + CFLAGS := $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 -EHsc, \ + LDFLAGS := $$(LDFLAGS_JDKEXE) -stack:655360, \ + LIBS := advapi32.lib user32.lib, \ OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/jdk.accessibility/jaccessinspector$1, \ OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_cmds/jdk.accessibility, \ PROGRAM := jaccessinspector$1, \ @@ -100,8 +101,9 @@ $$(eval $$(call SetupNativeCompilation,BUILD_JACCESSWALKER$1, \ SRC := $(TOPDIR)/jaccesswalker $(TOPDIR)/common \ $(TOPDIR)/toolscommon $(TOPDIR)/include/bridge, \ - CFLAGS :== $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 /EHsc, \ - LDFLAGS := $$(LDFLAGS_JDKEXE) /STACK:655360 Advapi32.lib Comctl32.lib Gdi32.lib User32.lib, \ + CFLAGS := $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 -EHsc, \ + LDFLAGS := $$(LDFLAGS_JDKEXE) -stack:655360, \ + LIBS := advapi32.lib comctl32.lib gdi32.lib user32.lib, \ OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/jdk.accessibility/jaccesswalker$1, \ OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_cmds/jdk.accessibility, \ PROGRAM := jaccesswalker$1, \ diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java --- a/jdk/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java Wed Jul 05 21:10:34 2017 +0200 @@ -29,7 +29,6 @@ import java.nio.channels.*; import java.nio.channels.spi.*; import java.util.*; -import sun.misc.*; /** * An implementation of Selector for Linux 2.6+ kernels that uses @@ -50,7 +49,7 @@ private Map fdToKey; // True if this Selector has been closed - private volatile boolean closed = false; + private volatile boolean closed; // Lock for interrupt triggering and clearing private final Object interruptLock = new Object(); diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java --- a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java Wed Jul 05 21:10:34 2017 +0200 @@ -322,19 +322,6 @@ bytesRead = 0; } - // process any pending requests - if ((nReady > 1) || (nReady == 1 && bytesRead == 0)) { - try { - read(socketpair[0], address, BUFFER_SIZE); - boolean shutdown = processRequests(); - if (shutdown) - break; - } catch (UnixException x) { - if (x.errno() != UnixConstants.EAGAIN) - throw x; - } - } - // iterate over buffer to decode events int offset = 0; while (offset < bytesRead) { @@ -369,6 +356,19 @@ offset += (SIZEOF_INOTIFY_EVENT + len); } + + // process any pending requests + if ((nReady > 1) || (nReady == 1 && bytesRead == 0)) { + try { + read(socketpair[0], address, BUFFER_SIZE); + boolean shutdown = processRequests(); + if (shutdown) + break; + } catch (UnixException x) { + if (x.errno() != UnixConstants.EAGAIN) + throw x; + } + } } } catch (UnixException x) { x.printStackTrace(); diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java --- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java Wed Jul 05 21:10:34 2017 +0200 @@ -93,7 +93,7 @@ // Instance of this provider, so we don't have to call the provider list // to find ourselves or run the risk of not being in the list. - private static volatile SunJCE instance = null; + private static volatile SunJCE instance; // lazy initialize SecureRandom to avoid potential recursion if Sun // provider has not been installed yet diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/io/File.java --- a/jdk/src/java.base/share/classes/java/io/File.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/io/File.java Wed Jul 05 21:10:34 2017 +0200 @@ -420,11 +420,11 @@ String scheme = uri.getScheme(); if ((scheme == null) || !scheme.equalsIgnoreCase("file")) throw new IllegalArgumentException("URI scheme is not \"file\""); - if (uri.getAuthority() != null) + if (uri.getRawAuthority() != null) throw new IllegalArgumentException("URI has an authority component"); - if (uri.getFragment() != null) + if (uri.getRawFragment() != null) throw new IllegalArgumentException("URI has a fragment component"); - if (uri.getQuery() != null) + if (uri.getRawQuery() != null) throw new IllegalArgumentException("URI has a query component"); String p = uri.getPath(); if (p.equals("")) diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/io/PipedInputStream.java --- a/jdk/src/java.base/share/classes/java/io/PipedInputStream.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/io/PipedInputStream.java Wed Jul 05 21:10:34 2017 +0200 @@ -48,9 +48,9 @@ * @since 1.0 */ public class PipedInputStream extends InputStream { - boolean closedByWriter = false; - volatile boolean closedByReader = false; - boolean connected = false; + boolean closedByWriter; + volatile boolean closedByReader; + boolean connected; /* REMIND: identification of the read and write sides needs to be more sophisticated. Either using thread groups (but what about diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java --- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java Wed Jul 05 21:10:34 2017 +0200 @@ -25,7 +25,7 @@ package java.lang; -import sun.misc.FloatingDecimal; +import jdk.internal.math.FloatingDecimal; import java.util.Arrays; import java.util.Spliterator; import java.util.stream.IntStream; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/Class.java --- a/jdk/src/java.base/share/classes/java/lang/Class.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/Class.java Wed Jul 05 21:10:34 2017 +0200 @@ -2518,7 +2518,7 @@ // Incremented by the VM on each call to JVM TI RedefineClasses() // that redefines this class or a superclass. - private transient volatile int classRedefinedCount = 0; + private transient volatile int classRedefinedCount; // Lazily create and cache ReflectionData private ReflectionData reflectionData() { @@ -3331,7 +3331,8 @@ * uncloned, cached, and shared by all callers. */ T[] getEnumConstantsShared() { - if (enumConstants == null) { + T[] constants = enumConstants; + if (constants == null) { if (!isEnum()) return null; try { final Method values = getMethod("values"); @@ -3344,16 +3345,16 @@ }); @SuppressWarnings("unchecked") T[] temporaryConstants = (T[])values.invoke(null); - enumConstants = temporaryConstants; + enumConstants = constants = temporaryConstants; } // These can happen when users concoct enum-like classes // that don't comply with the enum spec. catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException ex) { return null; } } - return enumConstants; + return constants; } - private transient volatile T[] enumConstants = null; + private transient volatile T[] enumConstants; /** * Returns a map from simple name to enum constant. This package-private @@ -3363,19 +3364,21 @@ * created lazily on first use. Typically it won't ever get created. */ Map enumConstantDirectory() { - if (enumConstantDirectory == null) { + Map directory = enumConstantDirectory; + if (directory == null) { T[] universe = getEnumConstantsShared(); if (universe == null) throw new IllegalArgumentException( getName() + " is not an enum type"); - Map m = new HashMap<>(2 * universe.length); - for (T constant : universe) - m.put(((Enum)constant).name(), constant); - enumConstantDirectory = m; + directory = new HashMap<>(2 * universe.length); + for (T constant : universe) { + directory.put(((Enum)constant).name(), constant); + } + enumConstantDirectory = directory; } - return enumConstantDirectory; + return directory; } - private transient volatile Map enumConstantDirectory = null; + private transient volatile Map enumConstantDirectory; /** * Casts an object to the class or interface represented diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/ClassLoader.java --- a/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Wed Jul 05 21:10:34 2017 +0200 @@ -45,11 +45,11 @@ import java.util.Set; import java.util.Stack; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Vector; import java.util.Hashtable; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; -import sun.misc.CompoundEnumeration; import sun.misc.Resource; import sun.misc.URLClassPath; import sun.reflect.CallerSensitive; @@ -2206,3 +2206,36 @@ return sys; } } + +/* + * A utility class that will enumerate over an array of enumerations. + */ +final class CompoundEnumeration implements Enumeration { + private final Enumeration[] enums; + private int index; + + public CompoundEnumeration(Enumeration[] enums) { + this.enums = enums; + } + + private boolean next() { + while (index < enums.length) { + if (enums[index] != null && enums[index].hasMoreElements()) { + return true; + } + index++; + } + return false; + } + + public boolean hasMoreElements() { + return next(); + } + + public E nextElement() { + if (!next()) { + throw new NoSuchElementException(); + } + return enums[index].nextElement(); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/Double.java --- a/jdk/src/java.base/share/classes/java/lang/Double.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/Double.java Wed Jul 05 21:10:34 2017 +0200 @@ -25,8 +25,8 @@ package java.lang; -import sun.misc.FloatingDecimal; -import sun.misc.DoubleConsts; +import jdk.internal.math.FloatingDecimal; +import jdk.internal.math.DoubleConsts; import jdk.internal.HotSpotIntrinsicCandidate; /** diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/Float.java --- a/jdk/src/java.base/share/classes/java/lang/Float.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/Float.java Wed Jul 05 21:10:34 2017 +0200 @@ -25,9 +25,9 @@ package java.lang; -import sun.misc.FloatingDecimal; -import sun.misc.FloatConsts; -import sun.misc.DoubleConsts; +import jdk.internal.math.FloatingDecimal; +import jdk.internal.math.FloatConsts; +import jdk.internal.math.DoubleConsts; import jdk.internal.HotSpotIntrinsicCandidate; /** diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/Math.java --- a/jdk/src/java.base/share/classes/java/lang/Math.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/Math.java Wed Jul 05 21:10:34 2017 +0200 @@ -26,8 +26,8 @@ package java.lang; import java.util.Random; -import sun.misc.FloatConsts; -import sun.misc.DoubleConsts; +import jdk.internal.math.FloatConsts; +import jdk.internal.math.DoubleConsts; import jdk.internal.HotSpotIntrinsicCandidate; /** diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/StrictMath.java --- a/jdk/src/java.base/share/classes/java/lang/StrictMath.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/StrictMath.java Wed Jul 05 21:10:34 2017 +0200 @@ -26,7 +26,7 @@ package java.lang; import java.util.Random; -import sun.misc.DoubleConsts; +import jdk.internal.math.DoubleConsts; import jdk.internal.HotSpotIntrinsicCandidate; /** diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/System.java --- a/jdk/src/java.base/share/classes/java/lang/System.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/System.java Wed Jul 05 21:10:34 2017 +0200 @@ -132,7 +132,7 @@ /* The security manager for the system. */ - private static volatile SecurityManager security = null; + private static volatile SecurityManager security; /** * Reassigns the "standard" input stream. @@ -206,7 +206,7 @@ setErr0(err); } - private static volatile Console cons = null; + private static volatile Console cons; /** * Returns the unique {@link java.io.Console Console} object associated * with the current Java virtual machine, if any. @@ -216,12 +216,13 @@ * @since 1.6 */ public static Console console() { - if (cons == null) { + Console c = cons; + if (c == null) { synchronized (System.class) { - cons = SharedSecrets.getJavaIOAccess().console(); + cons = c = SharedSecrets.getJavaIOAccess().console(); } } - return cons; + return c; } /** diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/Thread.java --- a/jdk/src/java.base/share/classes/java/lang/Thread.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/Thread.java Wed Jul 05 21:10:34 2017 +0200 @@ -207,12 +207,10 @@ /* For generating thread ID */ private static long threadSeqNumber; - /* Java thread status for tools, - * initialized to indicate thread 'not yet started' + /* + * Java thread status for tools, default indicates thread 'not yet started' */ - - private volatile int threadStatus = 0; - + private volatile int threadStatus; private static synchronized long nextThreadID() { return ++threadSeqNumber; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java --- a/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java Wed Jul 05 21:10:34 2017 +0200 @@ -53,7 +53,7 @@ private static class Lock { }; private Lock lock = new Lock(); - private volatile Reference head = null; + private volatile Reference head; private long queueLength = 0; boolean enqueue(Reference r) { /* Called only by Reference class */ diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/lang/reflect/Parameter.java --- a/jdk/src/java.base/share/classes/java/lang/reflect/Parameter.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Parameter.java Wed Jul 05 21:10:34 2017 +0200 @@ -205,7 +205,7 @@ return tmp; } - private transient volatile Type parameterTypeCache = null; + private transient volatile Type parameterTypeCache; /** * Returns a {@code Class} object that identifies the @@ -237,7 +237,7 @@ return executable.getAnnotatedParameterTypes()[index]; } - private transient volatile Class parameterClassCache = null; + private transient volatile Class parameterClassCache; /** * Returns {@code true} if this parameter is implicitly declared diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/math/BigInteger.java --- a/jdk/src/java.base/share/classes/java/math/BigInteger.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/math/BigInteger.java Wed Jul 05 21:10:34 2017 +0200 @@ -38,8 +38,8 @@ import java.util.Random; import java.util.concurrent.ThreadLocalRandom; -import sun.misc.DoubleConsts; -import sun.misc.FloatConsts; +import jdk.internal.math.DoubleConsts; +import jdk.internal.math.FloatConsts; import jdk.internal.HotSpotIntrinsicCandidate; /** diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/net/URI.java --- a/jdk/src/java.base/share/classes/java/net/URI.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/net/URI.java Wed Jul 05 21:10:34 2017 +0200 @@ -33,7 +33,6 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharsetDecoder; -import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; import java.nio.charset.CharacterCodingException; @@ -490,17 +489,17 @@ private transient String path; // null ==> opaque private transient String query; - // The remaining fields may be computed on demand - - private transient volatile String schemeSpecificPart; - private transient volatile int hash; // Zero ==> undefined - - private transient volatile String decodedUserInfo = null; - private transient volatile String decodedAuthority = null; - private transient volatile String decodedPath = null; - private transient volatile String decodedQuery = null; - private transient volatile String decodedFragment = null; - private transient volatile String decodedSchemeSpecificPart = null; + // The remaining fields may be computed on demand, which is safe even in + // the face of multiple threads racing to initialize them + private transient String schemeSpecificPart; + private transient int hash; // Zero ==> undefined + + private transient String decodedUserInfo; + private transient String decodedAuthority; + private transient String decodedPath; + private transient String decodedQuery; + private transient String decodedFragment; + private transient String decodedSchemeSpecificPart; /** * The string form of this URI. @@ -911,8 +910,7 @@ // either more fields or a more-obscure representation. if ((host != null) || (authority == null)) return this; - defineString(); - new Parser(string).parse(true); + new Parser(toString()).parse(true); return this; } @@ -1144,8 +1142,17 @@ * (never {@code null}) */ public String getRawSchemeSpecificPart() { - defineSchemeSpecificPart(); - return schemeSpecificPart; + String part = schemeSpecificPart; + if (part != null) { + return part; + } + StringBuilder sb = new StringBuilder(); + appendSchemeSpecificPart(sb, null, getAuthority(), getUserInfo(), + host, port, getPath(), getQuery()); + if (sb.length() == 0) { + return null; + } + return schemeSpecificPart = sb.toString(); } /** @@ -1160,9 +1167,11 @@ * (never {@code null}) */ public String getSchemeSpecificPart() { - if (decodedSchemeSpecificPart == null) - decodedSchemeSpecificPart = decode(getRawSchemeSpecificPart()); - return decodedSchemeSpecificPart; + String part = decodedSchemeSpecificPart; + if (part == null) { + decodedSchemeSpecificPart = part = decode(getRawSchemeSpecificPart()); + } + return part; } /** @@ -1193,9 +1202,11 @@ * or {@code null} if the authority is undefined */ public String getAuthority() { - if (decodedAuthority == null) - decodedAuthority = decode(authority); - return decodedAuthority; + String auth = decodedAuthority; + if ((auth == null) && (authority != null)) { + decodedAuthority = auth = decode(authority); + } + return auth; } /** @@ -1223,9 +1234,11 @@ * or {@code null} if the user information is undefined */ public String getUserInfo() { - if ((decodedUserInfo == null) && (userInfo != null)) - decodedUserInfo = decode(userInfo); - return decodedUserInfo; + String user = decodedUserInfo; + if ((user == null) && (userInfo != null)) { + decodedUserInfo = user = decode(userInfo); + } + return user; } /** @@ -1307,9 +1320,11 @@ * or {@code null} if the path is undefined */ public String getPath() { - if ((decodedPath == null) && (path != null)) - decodedPath = decode(path); - return decodedPath; + String decoded = decodedPath; + if ((decoded == null) && (path != null)) { + decodedPath = decoded = decode(path); + } + return decoded; } /** @@ -1336,9 +1351,11 @@ * or {@code null} if the query is undefined */ public String getQuery() { - if ((decodedQuery == null) && (query != null)) - decodedQuery = decode(query, false); - return decodedQuery; + String decoded = decodedQuery; + if ((decoded == null) && (query != null)) { + decodedQuery = decoded = decode(query, false); + } + return decoded; } /** @@ -1365,9 +1382,11 @@ * or {@code null} if the fragment is undefined */ public String getFragment() { - if ((decodedFragment == null) && (fragment != null)) - decodedFragment = decode(fragment, false); - return decodedFragment; + String decoded = decodedFragment; + if ((decoded == null) && (fragment != null)) { + decodedFragment = decoded = decode(fragment, false); + } + return decoded; } @@ -1453,24 +1472,27 @@ * @return A hash-code value for this URI */ public int hashCode() { - if (hash != 0) - return hash; - int h = hashIgnoringCase(0, scheme); - h = hash(h, fragment); - if (isOpaque()) { - h = hash(h, schemeSpecificPart); - } else { - h = hash(h, path); - h = hash(h, query); - if (host != null) { - h = hash(h, userInfo); - h = hashIgnoringCase(h, host); - h += 1949 * port; + int h = hash; + if (h == 0) { + h = hashIgnoringCase(0, scheme); + h = hash(h, fragment); + if (isOpaque()) { + h = hash(h, schemeSpecificPart); } else { - h = hash(h, authority); + h = hash(h, path); + h = hash(h, query); + if (host != null) { + h = hash(h, userInfo); + h = hashIgnoringCase(h, host); + h += 1949 * port; + } else { + h = hash(h, authority); + } + } + if (h != 0) { + hash = h; } } - hash = h; return h; } @@ -1600,8 +1622,59 @@ * @return The string form of this URI */ public String toString() { - defineString(); - return string; + String s = string; + if (s == null) { + s = defineString(); + } + return s; + } + + private String defineString() { + String s = string; + if (s != null) { + return s; + } + + StringBuilder sb = new StringBuilder(); + if (scheme != null) { + sb.append(scheme); + sb.append(':'); + } + if (isOpaque()) { + sb.append(schemeSpecificPart); + } else { + if (host != null) { + sb.append("//"); + if (userInfo != null) { + sb.append(userInfo); + sb.append('@'); + } + boolean needBrackets = ((host.indexOf(':') >= 0) + && !host.startsWith("[") + && !host.endsWith("]")); + if (needBrackets) sb.append('['); + sb.append(host); + if (needBrackets) sb.append(']'); + if (port != -1) { + sb.append(':'); + sb.append(port); + } + } else if (authority != null) { + sb.append("//"); + sb.append(authority); + } + if (path != null) + sb.append(path); + if (query != null) { + sb.append('?'); + sb.append(query); + } + } + if (fragment != null) { + sb.append('#'); + sb.append(fragment); + } + return string = sb.toString(); } /** @@ -1618,8 +1691,7 @@ * charset */ public String toASCIIString() { - defineString(); - return encode(string); + return encode(toString()); } @@ -1825,7 +1897,7 @@ } } - private void appendAuthority(StringBuffer sb, + private void appendAuthority(StringBuilder sb, String authority, String userInfo, String host, @@ -1875,7 +1947,7 @@ } } - private void appendSchemeSpecificPart(StringBuffer sb, + private void appendSchemeSpecificPart(StringBuilder sb, String opaquePart, String authority, String userInfo, @@ -1916,7 +1988,7 @@ } } - private void appendFragment(StringBuffer sb, String fragment) { + private void appendFragment(StringBuilder sb, String fragment) { if (fragment != null) { sb.append('#'); sb.append(quote(fragment, L_URIC, H_URIC)); @@ -1933,7 +2005,7 @@ String query, String fragment) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); if (scheme != null) { sb.append(scheme); sb.append(':'); @@ -1945,61 +2017,6 @@ return sb.toString(); } - private void defineSchemeSpecificPart() { - if (schemeSpecificPart != null) return; - StringBuffer sb = new StringBuffer(); - appendSchemeSpecificPart(sb, null, getAuthority(), getUserInfo(), - host, port, getPath(), getQuery()); - if (sb.length() == 0) return; - schemeSpecificPart = sb.toString(); - } - - private void defineString() { - if (string != null) return; - - StringBuilder sb = new StringBuilder(); - if (scheme != null) { - sb.append(scheme); - sb.append(':'); - } - if (isOpaque()) { - sb.append(schemeSpecificPart); - } else { - if (host != null) { - sb.append("//"); - if (userInfo != null) { - sb.append(userInfo); - sb.append('@'); - } - boolean needBrackets = ((host.indexOf(':') >= 0) - && !host.startsWith("[") - && !host.endsWith("]")); - if (needBrackets) sb.append('['); - sb.append(host); - if (needBrackets) sb.append(']'); - if (port != -1) { - sb.append(':'); - sb.append(port); - } - } else if (authority != null) { - sb.append("//"); - sb.append(authority); - } - if (path != null) - sb.append(path); - if (query != null) { - sb.append('?'); - sb.append(query); - } - } - if (fragment != null) { - sb.append('#'); - sb.append(fragment); - } - string = sb.toString(); - } - - // -- Normalization, resolution, and relativization -- // RFC2396 5.2 (6) @@ -2650,13 +2667,13 @@ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - private static void appendEscape(StringBuffer sb, byte b) { + private static void appendEscape(StringBuilder sb, byte b) { sb.append('%'); sb.append(hexDigits[(b >> 4) & 0x0f]); sb.append(hexDigits[(b >> 0) & 0x0f]); } - private static void appendEncoded(StringBuffer sb, char c) { + private static void appendEncoded(StringBuilder sb, char c) { ByteBuffer bb = null; try { bb = ThreadLocalCoders.encoderFor("UTF-8") @@ -2677,15 +2694,14 @@ // by the given mask pair // private static String quote(String s, long lowMask, long highMask) { - int n = s.length(); - StringBuffer sb = null; + StringBuilder sb = null; boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c < '\u0080') { if (!match(c, lowMask, highMask)) { if (sb == null) { - sb = new StringBuffer(); + sb = new StringBuilder(); sb.append(s, 0, i); } appendEscape(sb, (byte)c); @@ -2697,7 +2713,7 @@ && (Character.isSpaceChar(c) || Character.isISOControl(c))) { if (sb == null) { - sb = new StringBuffer(); + sb = new StringBuilder(); sb.append(s, 0, i); } appendEncoded(sb, c); @@ -2734,7 +2750,7 @@ assert false; } - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); while (bb.hasRemaining()) { int b = bb.get() & 0xff; if (b >= 0x80) @@ -2865,12 +2881,6 @@ fail("Expected " + expected, p); } - private void failExpecting(String expected, String prior, int p) - throws URISyntaxException - { - fail("Expected " + expected + " following " + prior, p); - } - // -- Simple access to the input string -- diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/net/URL.java --- a/jdk/src/java.base/share/classes/java/net/URL.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/net/URL.java Wed Jul 05 21:10:34 2017 +0200 @@ -310,7 +310,8 @@ * @param host the name of the host. * @param port the port number on the host. * @param file the file on the host - * @exception MalformedURLException if an unknown protocol is specified. + * @exception MalformedURLException if an unknown protocol or the port + * is a negative number other than -1 * @see java.lang.System#getProperty(java.lang.String) * @see java.net.URL#setURLStreamHandlerFactory( * java.net.URLStreamHandlerFactory) @@ -329,9 +330,9 @@ * name, {@code host} name, and {@code file} name. The * default port for the specified protocol is used. *

- * This method is equivalent to calling the four-argument - * constructor with the arguments being {@code protocol}, - * {@code host}, {@code -1}, and {@code file}. + * This constructor is equivalent to the four-argument + * constructor with the only difference of using the + * default port for the specified protocol. * * No validation of the inputs is performed by this constructor. * @@ -372,7 +373,8 @@ * @param port the port number on the host. * @param file the file on the host * @param handler the stream handler for the URL. - * @exception MalformedURLException if an unknown protocol is specified. + * @exception MalformedURLException if an unknown protocol or the port + is a negative number other than -1 * @exception SecurityException * if a security manager exists and its * {@code checkPermission} method doesn't allow @@ -446,7 +448,9 @@ * * @param spec the {@code String} to parse as a URL. * @exception MalformedURLException if no protocol is specified, or an - * unknown protocol is found, or {@code spec} is {@code null}. + * unknown protocol is found, or {@code spec} is {@code null}, + * or the parsed URL fails to comply with the specific syntax + * of the associated protocol. * @see java.net.URL#URL(java.net.URL, java.lang.String) */ public URL(String spec) throws MalformedURLException { @@ -493,7 +497,9 @@ * @param context the context in which to parse the specification. * @param spec the {@code String} to parse as a URL. * @exception MalformedURLException if no protocol is specified, or an - * unknown protocol is found, or {@code spec} is {@code null}. + * unknown protocol is found, or {@code spec} is {@code null}, + * or the parsed URL fails to comply with the specific syntax + * of the associated protocol. * @see java.net.URL#URL(java.lang.String, java.lang.String, * int, java.lang.String) * @see java.net.URLStreamHandler @@ -513,7 +519,9 @@ * @param spec the {@code String} to parse as a URL. * @param handler the stream handler for the URL. * @exception MalformedURLException if no protocol is specified, or an - * unknown protocol is found, or {@code spec} is {@code null}. + * unknown protocol is found, or {@code spec} is {@code null}, + * or the parsed URL fails to comply with the specific syntax + * of the associated protocol. * @exception SecurityException * if a security manager exists and its * {@code checkPermission} method doesn't allow diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/net/URLConnection.java --- a/jdk/src/java.base/share/classes/java/net/URLConnection.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/net/URLConnection.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1550,7 +1550,7 @@ } if (c1 == 0xFF && c2 == 0xD8 && c3 == 0xFF) { - if (c4 == 0xE0) { + if (c4 == 0xE0 || c4 == 0xEE) { return "image/jpeg"; } @@ -1565,10 +1565,6 @@ c11 == 0)) { return "image/jpeg"; } - - if (c4 == 0xEE) { - return "image/jpg"; - } } if (c1 == 0xD0 && c2 == 0xCF && c3 == 0x11 && c4 == 0xE0 && diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/nio/Bits.java --- a/jdk/src/java.base/share/classes/java/nio/Bits.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/nio/Bits.java Wed Jul 05 21:10:34 2017 +0200 @@ -25,9 +25,7 @@ package java.nio; -import java.security.AccessController; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.LongAdder; import jdk.internal.misc.JavaNioAccess; import jdk.internal.misc.JavaLangRefAccess; @@ -603,7 +601,8 @@ private static final AtomicLong reservedMemory = new AtomicLong(); private static final AtomicLong totalCapacity = new AtomicLong(); private static final AtomicLong count = new AtomicLong(); - private static volatile boolean memoryLimitSet = false; + private static volatile boolean memoryLimitSet; + // max. number of sleeps during try-reserving with exponentially // increasing delay before throwing OutOfMemoryError: // 1, 2, 4, 8, 16, 32, 64, 128, 256 (total 511 ms ~ 0.5 s) diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/nio/channels/SelectionKey.java --- a/jdk/src/java.base/share/classes/java/nio/channels/SelectionKey.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/nio/channels/SelectionKey.java Wed Jul 05 21:10:34 2017 +0200 @@ -26,8 +26,6 @@ package java.nio.channels; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import java.io.IOException; - /** * A token representing the registration of a {@link SelectableChannel} with a @@ -363,7 +361,7 @@ // -- Attachments -- - private volatile Object attachment = null; + private volatile Object attachment; private static final AtomicReferenceFieldUpdater attachmentUpdater = AtomicReferenceFieldUpdater.newUpdater( diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java --- a/jdk/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java Wed Jul 05 21:10:34 2017 +0200 @@ -29,11 +29,7 @@ package java.nio.channels.spi; import java.io.IOException; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; import java.nio.channels.*; -import java.security.AccessController; -import java.security.PrivilegedAction; import jdk.internal.misc.SharedSecrets; import sun.nio.ch.Interruptible; @@ -90,7 +86,7 @@ { private final Object closeLock = new Object(); - private volatile boolean open = true; + private volatile boolean closed; /** * Initializes a new instance of this class. @@ -110,9 +106,9 @@ */ public final void close() throws IOException { synchronized (closeLock) { - if (!open) + if (closed) return; - open = false; + closed = true; implCloseChannel(); } } @@ -136,7 +132,7 @@ protected abstract void implCloseChannel() throws IOException; public final boolean isOpen() { - return open; + return !closed; } @@ -158,9 +154,9 @@ interruptor = new Interruptible() { public void interrupt(Thread target) { synchronized (closeLock) { - if (!open) + if (closed) return; - open = false; + closed = true; interrupted = target; try { AbstractInterruptibleChannel.this.implCloseChannel(); @@ -202,7 +198,7 @@ this.interrupted = null; throw new ClosedByInterruptException(); } - if (!completed && !open) + if (!completed && closed) throw new AsynchronousCloseException(); } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/nio/charset/Charset.java --- a/jdk/src/java.base/share/classes/java/nio/charset/Charset.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/nio/charset/Charset.java Wed Jul 05 21:10:34 2017 +0200 @@ -276,7 +276,7 @@ /* -- Static methods -- */ - private static volatile String bugLevel = null; + private static volatile String bugLevel; static boolean atBugLevel(String bl) { // package-private String level = bugLevel; @@ -324,8 +324,8 @@ // Cache of the most-recently-returned charsets, // along with the names that were used to find them // - private static volatile Object[] cache1 = null; // "Level 1" cache - private static volatile Object[] cache2 = null; // "Level 2" cache + private static volatile Object[] cache1; // "Level 1" cache + private static volatile Object[] cache2; // "Level 2" cache private static void cache(String charsetName, Charset cs) { cache2 = cache1; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/security/SecureRandom.java --- a/jdk/src/java.base/share/classes/java/security/SecureRandom.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/security/SecureRandom.java Wed Jul 05 21:10:34 2017 +0200 @@ -124,7 +124,7 @@ private String algorithm; // Seed Generator - private static volatile SecureRandom seedGenerator = null; + private static volatile SecureRandom seedGenerator; /** * Constructs a secure random number generator (RNG) implementing the @@ -522,10 +522,12 @@ * @see #setSeed */ public static byte[] getSeed(int numBytes) { - if (seedGenerator == null) { - seedGenerator = new SecureRandom(); + SecureRandom seedGen = seedGenerator; + if (seedGen == null) { + seedGen = new SecureRandom(); + seedGenerator = seedGen; } - return seedGenerator.generateSeed(numBytes); + return seedGen.generateSeed(numBytes); } /** diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java --- a/jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java Wed Jul 05 21:10:34 2017 +0200 @@ -630,7 +630,9 @@ hashCode = 11 * hashCode + Arrays.hashCode(ampms); hashCode = 11 * hashCode + Arrays.deepHashCode(getZoneStringsWrapper()); hashCode = 11 * hashCode + Objects.hashCode(localPatternChars); - cachedHashCode = hashCode; + if (hashCode != 0) { + cachedHashCode = hashCode; + } } return hashCode; @@ -670,12 +672,12 @@ private static final ConcurrentMap> cachedInstances = new ConcurrentHashMap<>(3); - private transient int lastZoneIndex = 0; + private transient int lastZoneIndex; /** * Cached hash code */ - transient volatile int cachedHashCode = 0; + transient volatile int cachedHashCode; private void initializeData(Locale desiredLocale) { locale = desiredLocale; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/text/DecimalFormatSymbols.java --- a/jdk/src/java.base/share/classes/java/text/DecimalFormatSymbols.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/text/DecimalFormatSymbols.java Wed Jul 05 21:10:34 2017 +0200 @@ -42,14 +42,8 @@ import java.io.ObjectInputStream; import java.io.Serializable; import java.text.spi.DecimalFormatSymbolsProvider; -import java.util.ArrayList; import java.util.Currency; -import java.util.List; import java.util.Locale; -import java.util.MissingResourceException; -import java.util.ResourceBundle; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.LocaleServiceProviderPool; import sun.util.locale.provider.ResourceBundleBasedAdapter; @@ -875,7 +869,7 @@ // currency; only the ISO code is serialized. private transient Currency currency; - private transient volatile boolean currencyInitialized = false; + private transient volatile boolean currencyInitialized; // Proclaim JDK 1.1 FCS compatibility static final long serialVersionUID = 5772796243397350300L; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/text/DigitList.java --- a/jdk/src/java.base/share/classes/java/text/DigitList.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/text/DigitList.java Wed Jul 05 21:10:34 2017 +0200 @@ -41,7 +41,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; -import sun.misc.FloatingDecimal; +import jdk.internal.math.FloatingDecimal; /** * Digit List. Private to DecimalFormat. diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/time/LocalDate.java --- a/jdk/src/java.base/share/classes/java/time/LocalDate.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/time/LocalDate.java Wed Jul 05 21:10:34 2017 +0200 @@ -147,6 +147,10 @@ * This could be used by an application as a "far future" date. */ public static final LocalDate MAX = LocalDate.of(Year.MAX_VALUE, 12, 31); + /** + * The epoch year {@code LocalDate}, '1970-01-01'. + */ + public static final LocalDate EPOCH = LocalDate.of(1970, 1, 1); /** * Serialization version. @@ -1864,6 +1868,29 @@ return total - DAYS_0000_TO_1970; } + /** + * Converts this {@code LocalDate} to the number of seconds since the epoch + * of 1970-01-01T00:00:00Z. + *

+ * This combines this local date with the specified time and + * offset to calculate the epoch-second value, which is the + * number of elapsed seconds from 1970-01-01T00:00:00Z. + * Instants on the time-line after the epoch are positive, earlier + * are negative. + * + * @param time the local time, not null + * @param offset the zone offset, not null + * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative + * @since 9 + */ + public long toEpochSecond(LocalTime time, ZoneOffset offset) { + Objects.requireNonNull(time, "time"); + Objects.requireNonNull(offset, "offset"); + long secs = toEpochDay() * SECONDS_PER_DAY + time.toSecondOfDay(); + secs -= offset.getTotalSeconds(); + return secs; + } + //----------------------------------------------------------------------- /** * Compares this date to another date. diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/time/LocalTime.java --- a/jdk/src/java.base/share/classes/java/time/LocalTime.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/time/LocalTime.java Wed Jul 05 21:10:34 2017 +0200 @@ -1490,6 +1490,30 @@ return total; } + /** + * Converts this {@code LocalTime} to the number of seconds since the epoch + * of 1970-01-01T00:00:00Z. + *

+ * This combines this local time with the specified date and + * offset to calculate the epoch-second value, which is the + * number of elapsed seconds from 1970-01-01T00:00:00Z. + * Instants on the time-line after the epoch are positive, earlier + * are negative. + * + * @param date the local date, not null + * @param offset the zone offset, not null + * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative + * @since 9 + */ + public long toEpochSecond(LocalDate date, ZoneOffset offset) { + Objects.requireNonNull(date, "date"); + Objects.requireNonNull(offset, "offset"); + long epochDay = date.toEpochDay(); + long secs = epochDay * 86400 + toSecondOfDay(); + secs -= offset.getTotalSeconds(); + return secs; + } + //----------------------------------------------------------------------- /** * Compares this time to another time. diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/time/OffsetTime.java --- a/jdk/src/java.base/share/classes/java/time/OffsetTime.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/time/OffsetTime.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1232,6 +1232,28 @@ return nod - offsetNanos; } + /** + * Converts this {@code OffsetTime} to the number of seconds since the epoch + * of 1970-01-01T00:00:00Z. + *

+ * This combines this offset time with the specified date to calculate the + * epoch-second value, which is the number of elapsed seconds from + * 1970-01-01T00:00:00Z. + * Instants on the time-line after the epoch are positive, earlier + * are negative. + * + * @param date the localdate, not null + * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative + * @since 9 + */ + public long toEpochSecond(LocalDate date) { + Objects.requireNonNull(date, "date"); + long epochDay = date.toEpochDay(); + long secs = epochDay * 86400 + time.toSecondOfDay(); + secs -= offset.getTotalSeconds(); + return secs; + } + //----------------------------------------------------------------------- /** * Compares this {@code OffsetTime} to another time. diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/time/format/DateTimeFormatter.java --- a/jdk/src/java.base/share/classes/java/time/format/DateTimeFormatter.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/time/format/DateTimeFormatter.java Wed Jul 05 21:10:34 2017 +0200 @@ -80,6 +80,7 @@ import java.time.Period; import java.time.ZoneId; import java.time.ZoneOffset; +import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatterBuilder.CompositePrinterParser; @@ -473,6 +474,17 @@ * day-of-week was valid for the date. *

  • If an {@linkplain #parsedExcessDays() excess number of days} * was parsed then it is added to the date if a date is available. + *
  • If a second-based field is present, but {@code LocalTime} was not parsed, + * then the resolver ensures that milli, micro and nano second values are + * available to meet the contract of {@link ChronoField}. + * These will be set to zero if missing. + *
  • If both date and time were parsed and either an offset or zone is present, + * the field {@link ChronoField#INSTANT_SECONDS} is created. + * If an offset was parsed then the offset will be combined with the + * {@code LocalDateTime} to form the instant, with any zone ignored. + * If a {@code ZoneId} was parsed without an offset then the zone will be + * combined with the {@code LocalDateTime} to form the instant using the rules + * of {@link ChronoLocalDateTime#atZone(ZoneId)}. * * * @implSpec diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/time/format/Parsed.java --- a/jdk/src/java.base/share/classes/java/time/format/Parsed.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/time/format/Parsed.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -594,15 +594,16 @@ private void resolveInstant() { // add instant seconds if we have date, time and zone + // Offset (if present) will be given priority over the zone. if (date != null && time != null) { - if (zone != null) { - long instant = date.atTime(time).atZone(zone).getLong(ChronoField.INSTANT_SECONDS); + Long offsetSecs = fieldValues.get(OFFSET_SECONDS); + if (offsetSecs != null) { + ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSecs.intValue()); + long instant = date.atTime(time).atZone(offset).toEpochSecond(); fieldValues.put(INSTANT_SECONDS, instant); } else { - Long offsetSecs = fieldValues.get(OFFSET_SECONDS); - if (offsetSecs != null) { - ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSecs.intValue()); - long instant = date.atTime(time).atZone(offset).getLong(ChronoField.INSTANT_SECONDS); + if (zone != null) { + long instant = date.atTime(time).atZone(zone).toEpochSecond(); fieldValues.put(INSTANT_SECONDS, instant); } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/util/Formatter.java --- a/jdk/src/java.base/share/classes/java/util/Formatter.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/util/Formatter.java Wed Jul 05 21:10:34 2017 +0200 @@ -60,8 +60,8 @@ import java.time.temporal.TemporalQueries; import java.time.temporal.UnsupportedTemporalTypeException; -import sun.misc.DoubleConsts; -import sun.misc.FormattedFloatingDecimal; +import jdk.internal.math.DoubleConsts; +import jdk.internal.math.FormattedFloatingDecimal; /** * An interpreter for printf-style format strings. This class provides support diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/util/Locale.java --- a/jdk/src/java.base/share/classes/java/util/Locale.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/util/Locale.java Wed Jul 05 21:10:34 2017 +0200 @@ -62,7 +62,6 @@ import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.LocaleResources; import sun.util.locale.provider.LocaleServiceProviderPool; -import sun.util.locale.provider.ResourceBundleBasedAdapter; /** * A Locale object represents a specific geographical, political, @@ -2016,11 +2015,11 @@ /** * Calculated hashcode */ - private transient volatile int hashCodeValue = 0; + private transient volatile int hashCodeValue; private static volatile Locale defaultLocale = initDefault(); - private static volatile Locale defaultDisplayLocale = null; - private static volatile Locale defaultFormatLocale = null; + private static volatile Locale defaultDisplayLocale; + private static volatile Locale defaultFormatLocale; private transient volatile String languageTag; @@ -2207,9 +2206,9 @@ baseLocale.getRegion(), baseLocale.getVariant(), localeExtensions); } - private static volatile String[] isoLanguages = null; + private static volatile String[] isoLanguages; - private static volatile String[] isoCountries = null; + private static volatile String[] isoCountries; private static String convertOldISOCodes(String language) { // we accept both the old and the new ISO codes for the languages whose ISO @@ -2851,7 +2850,7 @@ private final String range; private final double weight; - private volatile int hash = 0; + private volatile int hash; /** * Constructs a {@code LanguageRange} using the given {@code range}. @@ -3108,14 +3107,17 @@ */ @Override public int hashCode() { - if (hash == 0) { - int result = 17; - result = 37*result + range.hashCode(); + int h = hash; + if (h == 0) { + h = 17; + h = 37*h + range.hashCode(); long bitsWeight = Double.doubleToLongBits(weight); - result = 37*result + (int)(bitsWeight ^ (bitsWeight >>> 32)); - hash = result; + h = 37*h + (int)(bitsWeight ^ (bitsWeight >>> 32)); + if (h != 0) { + hash = h; + } } - return hash; + return h; } /** diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/util/regex/Pattern.java --- a/jdk/src/java.base/share/classes/java/util/regex/Pattern.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/util/regex/Pattern.java Wed Jul 05 21:10:34 2017 +0200 @@ -950,7 +950,7 @@ * Boolean indicating this Pattern is compiled; this is necessary in order * to lazily compile deserialized Patterns. */ - private transient volatile boolean compiled = false; + private transient volatile boolean compiled; /** * The normalized pattern string. @@ -1332,7 +1332,6 @@ localCount = 0; // if length > 0, the Pattern is lazily compiled - compiled = false; if (pattern.length() == 0) { root = new Start(lastAccept); matchRoot = lastAccept; @@ -1377,7 +1376,6 @@ * equivalences of the characters. */ private void normalize() { - boolean inCharClass = false; int lastCodePoint = -1; // Convert pattern into normalized form @@ -1551,7 +1549,6 @@ // offset maintains the index in code units. loop: for(int x=0, offset=0; x=0; y--) { if (combClass[y] == combClass[x]) { continue loop; @@ -1566,8 +1563,7 @@ temp[index++] = prefix + sre; } String[] result = new String[index]; - for (int x=0; x namedGroups() { - if (namedGroups == null) - namedGroups = new HashMap<>(2); - return namedGroups; + Map groups = namedGroups; + if (groups == null) { + namedGroups = groups = new HashMap<>(2); + } + return groups; } /** diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/java/util/zip/ZipFile.java --- a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java Wed Jul 05 21:10:34 2017 +0200 @@ -72,7 +72,7 @@ class ZipFile implements ZipConstants, Closeable { private final String name; // zip file name - private volatile boolean closeRequested = false; + private volatile boolean closeRequested; private Source zsrc; private ZipCoder zc; @@ -366,7 +366,7 @@ } private class ZipFileInflaterInputStream extends InflaterInputStream { - private volatile boolean closeRequested = false; + private volatile boolean closeRequested; private boolean eof = false; private final ZipFileInputStream zfin; @@ -653,7 +653,7 @@ * (possibly compressed) zip file entry. */ private class ZipFileInputStream extends InputStream { - private volatile boolean closeRequested = false; + private volatile boolean closeRequested; private long pos; // current position within entry data protected long rem; // number of remaining bytes within entry protected long size; // uncompressed size of this entry diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java --- a/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java Wed Jul 05 21:10:34 2017 +0200 @@ -326,20 +326,22 @@ } // Do not expose this outside of this package. - private static volatile LoggerFinder provider = null; + private static volatile LoggerFinder provider; private static LoggerFinder accessLoggerFinder() { - if (provider == null) { + LoggerFinder prov = provider; + if (prov == null) { // no need to lock: it doesn't matter if we call // getLoggerFinder() twice - since LoggerFinder already caches // the result. // This is just an optimization to avoid the cost of calling // doPrivileged every time. final SecurityManager sm = System.getSecurityManager(); - provider = sm == null ? LoggerFinder.getLoggerFinder() : + prov = sm == null ? LoggerFinder.getLoggerFinder() : AccessController.doPrivileged( (PrivilegedAction)LoggerFinder::getLoggerFinder); + provider = prov; } - return provider; + return prov; } // Avoid using lambda here as lazy loggers could be created early diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/jdk/internal/math/DoubleConsts.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/math/DoubleConsts.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.math; + +/** + * This class contains additional constants documenting limits of the + * double type. + * + * @author Joseph D. Darcy + */ + +public class DoubleConsts { + /** + * Don't let anyone instantiate this class. + */ + private DoubleConsts() {} + + public static final double POSITIVE_INFINITY = java.lang.Double.POSITIVE_INFINITY; + public static final double NEGATIVE_INFINITY = java.lang.Double.NEGATIVE_INFINITY; + public static final double NaN = java.lang.Double.NaN; + public static final double MAX_VALUE = java.lang.Double.MAX_VALUE; + public static final double MIN_VALUE = java.lang.Double.MIN_VALUE; + + /** + * A constant holding the smallest positive normal value of type + * double, 2-1022. It is equal to the + * value returned by + * Double.longBitsToDouble(0x0010000000000000L). + * + * @since 1.5 + */ + public static final double MIN_NORMAL = 2.2250738585072014E-308; + + + /** + * The number of logical bits in the significand of a + * double number, including the implicit bit. + */ + public static final int SIGNIFICAND_WIDTH = 53; + + /** + * Maximum exponent a finite double number may have. + * It is equal to the value returned by + * Math.ilogb(Double.MAX_VALUE). + */ + public static final int MAX_EXPONENT = 1023; + + /** + * Minimum exponent a normalized double number may + * have. It is equal to the value returned by + * Math.ilogb(Double.MIN_NORMAL). + */ + public static final int MIN_EXPONENT = -1022; + + /** + * The exponent the smallest positive double + * subnormal value would have if it could be normalized.. + */ + public static final int MIN_SUB_EXPONENT = MIN_EXPONENT - + (SIGNIFICAND_WIDTH - 1); + + /** + * Bias used in representing a double exponent. + */ + public static final int EXP_BIAS = 1023; + + /** + * Bit mask to isolate the sign bit of a double. + */ + public static final long SIGN_BIT_MASK = 0x8000000000000000L; + + /** + * Bit mask to isolate the exponent field of a + * double. + */ + public static final long EXP_BIT_MASK = 0x7FF0000000000000L; + + /** + * Bit mask to isolate the significand field of a + * double. + */ + public static final long SIGNIF_BIT_MASK = 0x000FFFFFFFFFFFFFL; + + static { + // verify bit masks cover all bit positions and that the bit + // masks are non-overlapping + assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0L) && + (((SIGN_BIT_MASK & EXP_BIT_MASK) == 0L) && + ((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0L) && + ((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0L))); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,1508 @@ +/* + * 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 jdk.internal.math; + +import java.math.BigInteger; +import java.util.Arrays; +//@ model import org.jmlspecs.models.JMLMath; + +/** + * A simple big integer package specifically for floating point base conversion. + */ +public /*@ spec_bigint_math @*/ class FDBigInteger { + + // + // This class contains many comments that start with "/*@" mark. + // They are behavourial specification in + // the Java Modelling Language (JML): + // http://www.eecs.ucf.edu/~leavens/JML//index.shtml + // + + /*@ + @ public pure model static \bigint UNSIGNED(int v) { + @ return v >= 0 ? v : v + (((\bigint)1) << 32); + @ } + @ + @ public pure model static \bigint UNSIGNED(long v) { + @ return v >= 0 ? v : v + (((\bigint)1) << 64); + @ } + @ + @ public pure model static \bigint AP(int[] data, int len) { + @ return (\sum int i; 0 <= 0 && i < len; UNSIGNED(data[i]) << (i*32)); + @ } + @ + @ public pure model static \bigint pow52(int p5, int p2) { + @ ghost \bigint v = 1; + @ for (int i = 0; i < p5; i++) v *= 5; + @ return v << p2; + @ } + @ + @ public pure model static \bigint pow10(int p10) { + @ return pow52(p10, p10); + @ } + @*/ + + static final int[] SMALL_5_POW = { + 1, + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + + static final long[] LONG_5_POW = { + 1L, + 5L, + 5L * 5, + 5L * 5 * 5, + 5L * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + }; + + // Maximum size of cache of powers of 5 as FDBigIntegers. + private static final int MAX_FIVE_POW = 340; + + // Cache of big powers of 5 as FDBigIntegers. + private static final FDBigInteger POW_5_CACHE[]; + + // Initialize FDBigInteger cache of powers of 5. + static { + POW_5_CACHE = new FDBigInteger[MAX_FIVE_POW]; + int i = 0; + while (i < SMALL_5_POW.length) { + FDBigInteger pow5 = new FDBigInteger(new int[]{SMALL_5_POW[i]}, 0); + pow5.makeImmutable(); + POW_5_CACHE[i] = pow5; + i++; + } + FDBigInteger prev = POW_5_CACHE[i - 1]; + while (i < MAX_FIVE_POW) { + POW_5_CACHE[i] = prev = prev.mult(5); + prev.makeImmutable(); + i++; + } + } + + // Zero as an FDBigInteger. + public static final FDBigInteger ZERO = new FDBigInteger(new int[0], 0); + + // Ensure ZERO is immutable. + static { + ZERO.makeImmutable(); + } + + // Constant for casting an int to a long via bitwise AND. + private static final long LONG_MASK = 0xffffffffL; + + //@ spec_public non_null; + private int data[]; // value: data[0] is least significant + //@ spec_public; + private int offset; // number of least significant zero padding ints + //@ spec_public; + private int nWords; // data[nWords-1]!=0, all values above are zero + // if nWords==0 -> this FDBigInteger is zero + //@ spec_public; + private boolean isImmutable = false; + + /*@ + @ public invariant 0 <= nWords && nWords <= data.length && offset >= 0; + @ public invariant nWords == 0 ==> offset == 0; + @ public invariant nWords > 0 ==> data[nWords - 1] != 0; + @ public invariant (\forall int i; nWords <= i && i < data.length; data[i] == 0); + @ public pure model \bigint value() { + @ return AP(data, nWords) << (offset*32); + @ } + @*/ + + /** + * Constructs an FDBigInteger from data and padding. The + * data parameter has the least significant int at + * the zeroth index. The offset parameter gives the number of + * zero ints to be inferred below the least significant element + * of data. + * + * @param data An array containing all non-zero ints of the value. + * @param offset An offset indicating the number of zero ints to pad + * below the least significant element of data. + */ + /*@ + @ requires data != null && offset >= 0; + @ ensures this.value() == \old(AP(data, data.length) << (offset*32)); + @ ensures this.data == \old(data); + @*/ + private FDBigInteger(int[] data, int offset) { + this.data = data; + this.offset = offset; + this.nWords = data.length; + trimLeadingZeros(); + } + + /** + * Constructs an FDBigInteger from a starting value and some + * decimal digits. + * + * @param lValue The starting value. + * @param digits The decimal digits. + * @param kDigits The initial index into digits. + * @param nDigits The final index into digits. + */ + /*@ + @ requires digits != null; + @ requires 0 <= kDigits && kDigits <= nDigits && nDigits <= digits.length; + @ requires (\forall int i; 0 <= i && i < nDigits; '0' <= digits[i] && digits[i] <= '9'); + @ ensures this.value() == \old(lValue * pow10(nDigits - kDigits) + (\sum int i; kDigits <= i && i < nDigits; (digits[i] - '0') * pow10(nDigits - i - 1))); + @*/ + public FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits) { + int n = Math.max((nDigits + 8) / 9, 2); // estimate size needed. + data = new int[n]; // allocate enough space + data[0] = (int) lValue; // starting value + data[1] = (int) (lValue >>> 32); + offset = 0; + nWords = 2; + int i = kDigits; + int limit = nDigits - 5; // slurp digits 5 at a time. + int v; + while (i < limit) { + int ilim = i + 5; + v = (int) digits[i++] - (int) '0'; + while (i < ilim) { + v = 10 * v + (int) digits[i++] - (int) '0'; + } + multAddMe(100000, v); // ... where 100000 is 10^5. + } + int factor = 1; + v = 0; + while (i < nDigits) { + v = 10 * v + (int) digits[i++] - (int) '0'; + factor *= 10; + } + if (factor != 1) { + multAddMe(factor, v); + } + trimLeadingZeros(); + } + + /** + * Returns an FDBigInteger with the numerical value + * 5p5 * 2p2. + * + * @param p5 The exponent of the power-of-five factor. + * @param p2 The exponent of the power-of-two factor. + * @return 5p5 * 2p2 + */ + /*@ + @ requires p5 >= 0 && p2 >= 0; + @ assignable \nothing; + @ ensures \result.value() == \old(pow52(p5, p2)); + @*/ + public static FDBigInteger valueOfPow52(int p5, int p2) { + if (p5 != 0) { + if (p2 == 0) { + return big5pow(p5); + } else if (p5 < SMALL_5_POW.length) { + int pow5 = SMALL_5_POW[p5]; + int wordcount = p2 >> 5; + int bitcount = p2 & 0x1f; + if (bitcount == 0) { + return new FDBigInteger(new int[]{pow5}, wordcount); + } else { + return new FDBigInteger(new int[]{ + pow5 << bitcount, + pow5 >>> (32 - bitcount) + }, wordcount); + } + } else { + return big5pow(p5).leftShift(p2); + } + } else { + return valueOfPow2(p2); + } + } + + /** + * Returns an FDBigInteger with the numerical value + * value * 5p5 * 2p2. + * + * @param value The constant factor. + * @param p5 The exponent of the power-of-five factor. + * @param p2 The exponent of the power-of-two factor. + * @return value * 5p5 * 2p2 + */ + /*@ + @ requires p5 >= 0 && p2 >= 0; + @ assignable \nothing; + @ ensures \result.value() == \old(UNSIGNED(value) * pow52(p5, p2)); + @*/ + public static FDBigInteger valueOfMulPow52(long value, int p5, int p2) { + assert p5 >= 0 : p5; + assert p2 >= 0 : p2; + int v0 = (int) value; + int v1 = (int) (value >>> 32); + int wordcount = p2 >> 5; + int bitcount = p2 & 0x1f; + if (p5 != 0) { + if (p5 < SMALL_5_POW.length) { + long pow5 = SMALL_5_POW[p5] & LONG_MASK; + long carry = (v0 & LONG_MASK) * pow5; + v0 = (int) carry; + carry >>>= 32; + carry = (v1 & LONG_MASK) * pow5 + carry; + v1 = (int) carry; + int v2 = (int) (carry >>> 32); + if (bitcount == 0) { + return new FDBigInteger(new int[]{v0, v1, v2}, wordcount); + } else { + return new FDBigInteger(new int[]{ + v0 << bitcount, + (v1 << bitcount) | (v0 >>> (32 - bitcount)), + (v2 << bitcount) | (v1 >>> (32 - bitcount)), + v2 >>> (32 - bitcount) + }, wordcount); + } + } else { + FDBigInteger pow5 = big5pow(p5); + int[] r; + if (v1 == 0) { + r = new int[pow5.nWords + 1 + ((p2 != 0) ? 1 : 0)]; + mult(pow5.data, pow5.nWords, v0, r); + } else { + r = new int[pow5.nWords + 2 + ((p2 != 0) ? 1 : 0)]; + mult(pow5.data, pow5.nWords, v0, v1, r); + } + return (new FDBigInteger(r, pow5.offset)).leftShift(p2); + } + } else if (p2 != 0) { + if (bitcount == 0) { + return new FDBigInteger(new int[]{v0, v1}, wordcount); + } else { + return new FDBigInteger(new int[]{ + v0 << bitcount, + (v1 << bitcount) | (v0 >>> (32 - bitcount)), + v1 >>> (32 - bitcount) + }, wordcount); + } + } + return new FDBigInteger(new int[]{v0, v1}, 0); + } + + /** + * Returns an FDBigInteger with the numerical value + * 2p2. + * + * @param p2 The exponent of 2. + * @return 2p2 + */ + /*@ + @ requires p2 >= 0; + @ assignable \nothing; + @ ensures \result.value() == pow52(0, p2); + @*/ + private static FDBigInteger valueOfPow2(int p2) { + int wordcount = p2 >> 5; + int bitcount = p2 & 0x1f; + return new FDBigInteger(new int[]{1 << bitcount}, wordcount); + } + + /** + * Removes all leading zeros from this FDBigInteger adjusting + * the offset and number of non-zero leading words accordingly. + */ + /*@ + @ requires data != null; + @ requires 0 <= nWords && nWords <= data.length && offset >= 0; + @ requires nWords == 0 ==> offset == 0; + @ ensures nWords == 0 ==> offset == 0; + @ ensures nWords > 0 ==> data[nWords - 1] != 0; + @*/ + private /*@ helper @*/ void trimLeadingZeros() { + int i = nWords; + if (i > 0 && (data[--i] == 0)) { + //for (; i > 0 && data[i - 1] == 0; i--) ; + while(i > 0 && data[i - 1] == 0) { + i--; + } + this.nWords = i; + if (i == 0) { // all words are zero + this.offset = 0; + } + } + } + + /** + * Retrieves the normalization bias of the FDBigIntger. The + * normalization bias is a left shift such that after it the highest word + * of the value will have the 4 highest bits equal to zero: + * {@code (highestWord & 0xf0000000) == 0}, but the next bit should be 1 + * {@code (highestWord & 0x08000000) != 0}. + * + * @return The normalization bias. + */ + /*@ + @ requires this.value() > 0; + @*/ + public /*@ pure @*/ int getNormalizationBias() { + if (nWords == 0) { + throw new IllegalArgumentException("Zero value cannot be normalized"); + } + int zeros = Integer.numberOfLeadingZeros(data[nWords - 1]); + return (zeros < 4) ? 28 + zeros : zeros - 4; + } + + // TODO: Why is anticount param needed if it is always 32 - bitcount? + /** + * Left shifts the contents of one int array into another. + * + * @param src The source array. + * @param idx The initial index of the source array. + * @param result The destination array. + * @param bitcount The left shift. + * @param anticount The left anti-shift, e.g., 32-bitcount. + * @param prev The prior source value. + */ + /*@ + @ requires 0 < bitcount && bitcount < 32 && anticount == 32 - bitcount; + @ requires src.length >= idx && result.length > idx; + @ assignable result[*]; + @ ensures AP(result, \old(idx + 1)) == \old((AP(src, idx) + UNSIGNED(prev) << (idx*32)) << bitcount); + @*/ + private static void leftShift(int[] src, int idx, int result[], int bitcount, int anticount, int prev){ + for (; idx > 0; idx--) { + int v = (prev << bitcount); + prev = src[idx - 1]; + v |= (prev >>> anticount); + result[idx] = v; + } + int v = prev << bitcount; + result[0] = v; + } + + /** + * Shifts this FDBigInteger to the left. The shift is performed + * in-place unless the FDBigInteger is immutable in which case + * a new instance of FDBigInteger is returned. + * + * @param shift The number of bits to shift left. + * @return The shifted FDBigInteger. + */ + /*@ + @ requires this.value() == 0 || shift == 0; + @ assignable \nothing; + @ ensures \result == this; + @ + @ also + @ + @ requires this.value() > 0 && shift > 0 && this.isImmutable; + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() << shift); + @ + @ also + @ + @ requires this.value() > 0 && shift > 0 && this.isImmutable; + @ assignable \nothing; + @ ensures \result == this; + @ ensures \result.value() == \old(this.value() << shift); + @*/ + public FDBigInteger leftShift(int shift) { + if (shift == 0 || nWords == 0) { + return this; + } + int wordcount = shift >> 5; + int bitcount = shift & 0x1f; + if (this.isImmutable) { + if (bitcount == 0) { + return new FDBigInteger(Arrays.copyOf(data, nWords), offset + wordcount); + } else { + int anticount = 32 - bitcount; + int idx = nWords - 1; + int prev = data[idx]; + int hi = prev >>> anticount; + int[] result; + if (hi != 0) { + result = new int[nWords + 1]; + result[nWords] = hi; + } else { + result = new int[nWords]; + } + leftShift(data,idx,result,bitcount,anticount,prev); + return new FDBigInteger(result, offset + wordcount); + } + } else { + if (bitcount != 0) { + int anticount = 32 - bitcount; + if ((data[0] << bitcount) == 0) { + int idx = 0; + int prev = data[idx]; + for (; idx < nWords - 1; idx++) { + int v = (prev >>> anticount); + prev = data[idx + 1]; + v |= (prev << bitcount); + data[idx] = v; + } + int v = prev >>> anticount; + data[idx] = v; + if(v==0) { + nWords--; + } + offset++; + } else { + int idx = nWords - 1; + int prev = data[idx]; + int hi = prev >>> anticount; + int[] result = data; + int[] src = data; + if (hi != 0) { + if(nWords == data.length) { + data = result = new int[nWords + 1]; + } + result[nWords++] = hi; + } + leftShift(src,idx,result,bitcount,anticount,prev); + } + } + offset += wordcount; + return this; + } + } + + /** + * Returns the number of ints this FDBigInteger represents. + * + * @return Number of ints required to represent this FDBigInteger. + */ + /*@ + @ requires this.value() == 0; + @ ensures \result == 0; + @ + @ also + @ + @ requires this.value() > 0; + @ ensures ((\bigint)1) << (\result - 1) <= this.value() && this.value() <= ((\bigint)1) << \result; + @*/ + private /*@ pure @*/ int size() { + return nWords + offset; + } + + + /** + * Computes + *
    +     * q = (int)( this / S )
    +     * this = 10 * ( this mod S )
    +     * Return q.
    +     * 
    + * This is the iteration step of digit development for output. + * We assume that S has been normalized, as above, and that + * "this" has been left-shifted accordingly. + * Also assumed, of course, is that the result, q, can be expressed + * as an integer, {@code 0 <= q < 10}. + * + * @param S The divisor of this FDBigInteger. + * @return q = (int)(this / S). + */ + /*@ + @ requires !this.isImmutable; + @ requires this.size() <= S.size(); + @ requires this.data.length + this.offset >= S.size(); + @ requires S.value() >= ((\bigint)1) << (S.size()*32 - 4); + @ assignable this.nWords, this.offset, this.data, this.data[*]; + @ ensures \result == \old(this.value() / S.value()); + @ ensures this.value() == \old(10 * (this.value() % S.value())); + @*/ + public int quoRemIteration(FDBigInteger S) throws IllegalArgumentException { + assert !this.isImmutable : "cannot modify immutable value"; + // ensure that this and S have the same number of + // digits. If S is properly normalized and q < 10 then + // this must be so. + int thSize = this.size(); + int sSize = S.size(); + if (thSize < sSize) { + // this value is significantly less than S, result of division is zero. + // just mult this by 10. + int p = multAndCarryBy10(this.data, this.nWords, this.data); + if(p!=0) { + this.data[nWords++] = p; + } else { + trimLeadingZeros(); + } + return 0; + } else if (thSize > sSize) { + throw new IllegalArgumentException("disparate values"); + } + // estimate q the obvious way. We will usually be + // right. If not, then we're only off by a little and + // will re-add. + long q = (this.data[this.nWords - 1] & LONG_MASK) / (S.data[S.nWords - 1] & LONG_MASK); + long diff = multDiffMe(q, S); + if (diff != 0L) { + //@ assert q != 0; + //@ assert this.offset == \old(Math.min(this.offset, S.offset)); + //@ assert this.offset <= S.offset; + + // q is too big. + // add S back in until this turns +. This should + // not be very many times! + long sum = 0L; + int tStart = S.offset - this.offset; + //@ assert tStart >= 0; + int[] sd = S.data; + int[] td = this.data; + while (sum == 0L) { + for (int sIndex = 0, tIndex = tStart; tIndex < this.nWords; sIndex++, tIndex++) { + sum += (td[tIndex] & LONG_MASK) + (sd[sIndex] & LONG_MASK); + td[tIndex] = (int) sum; + sum >>>= 32; // Signed or unsigned, answer is 0 or 1 + } + // + // Originally the following line read + // "if ( sum !=0 && sum != -1 )" + // but that would be wrong, because of the + // treatment of the two values as entirely unsigned, + // it would be impossible for a carry-out to be interpreted + // as -1 -- it would have to be a single-bit carry-out, or +1. + // + assert sum == 0 || sum == 1 : sum; // carry out of division correction + q -= 1; + } + } + // finally, we can multiply this by 10. + // it cannot overflow, right, as the high-order word has + // at least 4 high-order zeros! + int p = multAndCarryBy10(this.data, this.nWords, this.data); + assert p == 0 : p; // Carry out of *10 + trimLeadingZeros(); + return (int) q; + } + + /** + * Multiplies this FDBigInteger by 10. The operation will be + * performed in place unless the FDBigInteger is immutable in + * which case a new FDBigInteger will be returned. + * + * @return The FDBigInteger multiplied by 10. + */ + /*@ + @ requires this.value() == 0; + @ assignable \nothing; + @ ensures \result == this; + @ + @ also + @ + @ requires this.value() > 0 && this.isImmutable; + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() * 10); + @ + @ also + @ + @ requires this.value() > 0 && !this.isImmutable; + @ assignable this.nWords, this.data, this.data[*]; + @ ensures \result == this; + @ ensures \result.value() == \old(this.value() * 10); + @*/ + public FDBigInteger multBy10() { + if (nWords == 0) { + return this; + } + if (isImmutable) { + int[] res = new int[nWords + 1]; + res[nWords] = multAndCarryBy10(data, nWords, res); + return new FDBigInteger(res, offset); + } else { + int p = multAndCarryBy10(this.data, this.nWords, this.data); + if (p != 0) { + if (nWords == data.length) { + if (data[0] == 0) { + System.arraycopy(data, 1, data, 0, --nWords); + offset++; + } else { + data = Arrays.copyOf(data, data.length + 1); + } + } + data[nWords++] = p; + } else { + trimLeadingZeros(); + } + return this; + } + } + + /** + * Multiplies this FDBigInteger by + * 5p5 * 2p2. The operation will be + * performed in place if possible, otherwise a new FDBigInteger + * will be returned. + * + * @param p5 The exponent of the power-of-five factor. + * @param p2 The exponent of the power-of-two factor. + * @return The multiplication result. + */ + /*@ + @ requires this.value() == 0 || p5 == 0 && p2 == 0; + @ assignable \nothing; + @ ensures \result == this; + @ + @ also + @ + @ requires this.value() > 0 && (p5 > 0 && p2 >= 0 || p5 == 0 && p2 > 0 && this.isImmutable); + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() * pow52(p5, p2)); + @ + @ also + @ + @ requires this.value() > 0 && p5 == 0 && p2 > 0 && !this.isImmutable; + @ assignable this.nWords, this.data, this.data[*]; + @ ensures \result == this; + @ ensures \result.value() == \old(this.value() * pow52(p5, p2)); + @*/ + public FDBigInteger multByPow52(int p5, int p2) { + if (this.nWords == 0) { + return this; + } + FDBigInteger res = this; + if (p5 != 0) { + int[] r; + int extraSize = (p2 != 0) ? 1 : 0; + if (p5 < SMALL_5_POW.length) { + r = new int[this.nWords + 1 + extraSize]; + mult(this.data, this.nWords, SMALL_5_POW[p5], r); + res = new FDBigInteger(r, this.offset); + } else { + FDBigInteger pow5 = big5pow(p5); + r = new int[this.nWords + pow5.size() + extraSize]; + mult(this.data, this.nWords, pow5.data, pow5.nWords, r); + res = new FDBigInteger(r, this.offset + pow5.offset); + } + } + return res.leftShift(p2); + } + + /** + * Multiplies two big integers represented as int arrays. + * + * @param s1 The first array factor. + * @param s1Len The number of elements of s1 to use. + * @param s2 The second array factor. + * @param s2Len The number of elements of s2 to use. + * @param dst The product array. + */ + /*@ + @ requires s1 != dst && s2 != dst; + @ requires s1.length >= s1Len && s2.length >= s2Len && dst.length >= s1Len + s2Len; + @ assignable dst[0 .. s1Len + s2Len - 1]; + @ ensures AP(dst, s1Len + s2Len) == \old(AP(s1, s1Len) * AP(s2, s2Len)); + @*/ + private static void mult(int[] s1, int s1Len, int[] s2, int s2Len, int[] dst) { + for (int i = 0; i < s1Len; i++) { + long v = s1[i] & LONG_MASK; + long p = 0L; + for (int j = 0; j < s2Len; j++) { + p += (dst[i + j] & LONG_MASK) + v * (s2[j] & LONG_MASK); + dst[i + j] = (int) p; + p >>>= 32; + } + dst[i + s2Len] = (int) p; + } + } + + /** + * Subtracts the supplied FDBigInteger subtrahend from this + * FDBigInteger. Assert that the result is positive. + * If the subtrahend is immutable, store the result in this(minuend). + * If this(minuend) is immutable a new FDBigInteger is created. + * + * @param subtrahend The FDBigInteger to be subtracted. + * @return This FDBigInteger less the subtrahend. + */ + /*@ + @ requires this.isImmutable; + @ requires this.value() >= subtrahend.value(); + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() - subtrahend.value()); + @ + @ also + @ + @ requires !subtrahend.isImmutable; + @ requires this.value() >= subtrahend.value(); + @ assignable this.nWords, this.offset, this.data, this.data[*]; + @ ensures \result == this; + @ ensures \result.value() == \old(this.value() - subtrahend.value()); + @*/ + public FDBigInteger leftInplaceSub(FDBigInteger subtrahend) { + assert this.size() >= subtrahend.size() : "result should be positive"; + FDBigInteger minuend; + if (this.isImmutable) { + minuend = new FDBigInteger(this.data.clone(), this.offset); + } else { + minuend = this; + } + int offsetDiff = subtrahend.offset - minuend.offset; + int[] sData = subtrahend.data; + int[] mData = minuend.data; + int subLen = subtrahend.nWords; + int minLen = minuend.nWords; + if (offsetDiff < 0) { + // need to expand minuend + int rLen = minLen - offsetDiff; + if (rLen < mData.length) { + System.arraycopy(mData, 0, mData, -offsetDiff, minLen); + Arrays.fill(mData, 0, -offsetDiff, 0); + } else { + int[] r = new int[rLen]; + System.arraycopy(mData, 0, r, -offsetDiff, minLen); + minuend.data = mData = r; + } + minuend.offset = subtrahend.offset; + minuend.nWords = minLen = rLen; + offsetDiff = 0; + } + long borrow = 0L; + int mIndex = offsetDiff; + for (int sIndex = 0; sIndex < subLen && mIndex < minLen; sIndex++, mIndex++) { + long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow; + mData[mIndex] = (int) diff; + borrow = diff >> 32; // signed shift + } + for (; borrow != 0 && mIndex < minLen; mIndex++) { + long diff = (mData[mIndex] & LONG_MASK) + borrow; + mData[mIndex] = (int) diff; + borrow = diff >> 32; // signed shift + } + assert borrow == 0L : borrow; // borrow out of subtract, + // result should be positive + minuend.trimLeadingZeros(); + return minuend; + } + + /** + * Subtracts the supplied FDBigInteger subtrahend from this + * FDBigInteger. Assert that the result is positive. + * If the this(minuend) is immutable, store the result in subtrahend. + * If subtrahend is immutable a new FDBigInteger is created. + * + * @param subtrahend The FDBigInteger to be subtracted. + * @return This FDBigInteger less the subtrahend. + */ + /*@ + @ requires subtrahend.isImmutable; + @ requires this.value() >= subtrahend.value(); + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() - subtrahend.value()); + @ + @ also + @ + @ requires !subtrahend.isImmutable; + @ requires this.value() >= subtrahend.value(); + @ assignable subtrahend.nWords, subtrahend.offset, subtrahend.data, subtrahend.data[*]; + @ ensures \result == subtrahend; + @ ensures \result.value() == \old(this.value() - subtrahend.value()); + @*/ + public FDBigInteger rightInplaceSub(FDBigInteger subtrahend) { + assert this.size() >= subtrahend.size() : "result should be positive"; + FDBigInteger minuend = this; + if (subtrahend.isImmutable) { + subtrahend = new FDBigInteger(subtrahend.data.clone(), subtrahend.offset); + } + int offsetDiff = minuend.offset - subtrahend.offset; + int[] sData = subtrahend.data; + int[] mData = minuend.data; + int subLen = subtrahend.nWords; + int minLen = minuend.nWords; + if (offsetDiff < 0) { + int rLen = minLen; + if (rLen < sData.length) { + System.arraycopy(sData, 0, sData, -offsetDiff, subLen); + Arrays.fill(sData, 0, -offsetDiff, 0); + } else { + int[] r = new int[rLen]; + System.arraycopy(sData, 0, r, -offsetDiff, subLen); + subtrahend.data = sData = r; + } + subtrahend.offset = minuend.offset; + subLen -= offsetDiff; + offsetDiff = 0; + } else { + int rLen = minLen + offsetDiff; + if (rLen >= sData.length) { + subtrahend.data = sData = Arrays.copyOf(sData, rLen); + } + } + //@ assert minuend == this && minuend.value() == \old(this.value()); + //@ assert mData == minuend.data && minLen == minuend.nWords; + //@ assert subtrahend.offset + subtrahend.data.length >= minuend.size(); + //@ assert sData == subtrahend.data; + //@ assert AP(subtrahend.data, subtrahend.data.length) << subtrahend.offset == \old(subtrahend.value()); + //@ assert subtrahend.offset == Math.min(\old(this.offset), minuend.offset); + //@ assert offsetDiff == minuend.offset - subtrahend.offset; + //@ assert 0 <= offsetDiff && offsetDiff + minLen <= sData.length; + int sIndex = 0; + long borrow = 0L; + for (; sIndex < offsetDiff; sIndex++) { + long diff = 0L - (sData[sIndex] & LONG_MASK) + borrow; + sData[sIndex] = (int) diff; + borrow = diff >> 32; // signed shift + } + //@ assert sIndex == offsetDiff; + for (int mIndex = 0; mIndex < minLen; sIndex++, mIndex++) { + //@ assert sIndex == offsetDiff + mIndex; + long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow; + sData[sIndex] = (int) diff; + borrow = diff >> 32; // signed shift + } + assert borrow == 0L : borrow; // borrow out of subtract, + // result should be positive + subtrahend.nWords = sIndex; + subtrahend.trimLeadingZeros(); + return subtrahend; + + } + + /** + * Determines whether all elements of an array are zero for all indices less + * than a given index. + * + * @param a The array to be examined. + * @param from The index strictly below which elements are to be examined. + * @return Zero if all elements in range are zero, 1 otherwise. + */ + /*@ + @ requires 0 <= from && from <= a.length; + @ ensures \result == (AP(a, from) == 0 ? 0 : 1); + @*/ + private /*@ pure @*/ static int checkZeroTail(int[] a, int from) { + while (from > 0) { + if (a[--from] != 0) { + return 1; + } + } + return 0; + } + + /** + * Compares the parameter with this FDBigInteger. Returns an + * integer accordingly as: + *
    {@code
    +     * > 0: this > other
    +     *   0: this == other
    +     * < 0: this < other
    +     * }
    + * + * @param other The FDBigInteger to compare. + * @return A negative value, zero, or a positive value according to the + * result of the comparison. + */ + /*@ + @ ensures \result == (this.value() < other.value() ? -1 : this.value() > other.value() ? +1 : 0); + @*/ + public /*@ pure @*/ int cmp(FDBigInteger other) { + int aSize = nWords + offset; + int bSize = other.nWords + other.offset; + if (aSize > bSize) { + return 1; + } else if (aSize < bSize) { + return -1; + } + int aLen = nWords; + int bLen = other.nWords; + while (aLen > 0 && bLen > 0) { + int a = data[--aLen]; + int b = other.data[--bLen]; + if (a != b) { + return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1; + } + } + if (aLen > 0) { + return checkZeroTail(data, aLen); + } + if (bLen > 0) { + return -checkZeroTail(other.data, bLen); + } + return 0; + } + + /** + * Compares this FDBigInteger with + * 5p5 * 2p2. + * Returns an integer accordingly as: + *
    {@code
    +     * > 0: this > other
    +     *   0: this == other
    +     * < 0: this < other
    +     * }
    + * @param p5 The exponent of the power-of-five factor. + * @param p2 The exponent of the power-of-two factor. + * @return A negative value, zero, or a positive value according to the + * result of the comparison. + */ + /*@ + @ requires p5 >= 0 && p2 >= 0; + @ ensures \result == (this.value() < pow52(p5, p2) ? -1 : this.value() > pow52(p5, p2) ? +1 : 0); + @*/ + public /*@ pure @*/ int cmpPow52(int p5, int p2) { + if (p5 == 0) { + int wordcount = p2 >> 5; + int bitcount = p2 & 0x1f; + int size = this.nWords + this.offset; + if (size > wordcount + 1) { + return 1; + } else if (size < wordcount + 1) { + return -1; + } + int a = this.data[this.nWords -1]; + int b = 1 << bitcount; + if (a != b) { + return ( (a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1; + } + return checkZeroTail(this.data, this.nWords - 1); + } + return this.cmp(big5pow(p5).leftShift(p2)); + } + + /** + * Compares this FDBigInteger with x + y. Returns a + * value according to the comparison as: + *
    {@code
    +     * -1: this <  x + y
    +     *  0: this == x + y
    +     *  1: this >  x + y
    +     * }
    + * @param x The first addend of the sum to compare. + * @param y The second addend of the sum to compare. + * @return -1, 0, or 1 according to the result of the comparison. + */ + /*@ + @ ensures \result == (this.value() < x.value() + y.value() ? -1 : this.value() > x.value() + y.value() ? +1 : 0); + @*/ + public /*@ pure @*/ int addAndCmp(FDBigInteger x, FDBigInteger y) { + FDBigInteger big; + FDBigInteger small; + int xSize = x.size(); + int ySize = y.size(); + int bSize; + int sSize; + if (xSize >= ySize) { + big = x; + small = y; + bSize = xSize; + sSize = ySize; + } else { + big = y; + small = x; + bSize = ySize; + sSize = xSize; + } + int thSize = this.size(); + if (bSize == 0) { + return thSize == 0 ? 0 : 1; + } + if (sSize == 0) { + return this.cmp(big); + } + if (bSize > thSize) { + return -1; + } + if (bSize + 1 < thSize) { + return 1; + } + long top = (big.data[big.nWords - 1] & LONG_MASK); + if (sSize == bSize) { + top += (small.data[small.nWords - 1] & LONG_MASK); + } + if ((top >>> 32) == 0) { + if (((top + 1) >>> 32) == 0) { + // good case - no carry extension + if (bSize < thSize) { + return 1; + } + // here sum.nWords == this.nWords + long v = (this.data[this.nWords - 1] & LONG_MASK); + if (v < top) { + return -1; + } + if (v > top + 1) { + return 1; + } + } + } else { // (top>>>32)!=0 guaranteed carry extension + if (bSize + 1 > thSize) { + return -1; + } + // here sum.nWords == this.nWords + top >>>= 32; + long v = (this.data[this.nWords - 1] & LONG_MASK); + if (v < top) { + return -1; + } + if (v > top + 1) { + return 1; + } + } + return this.cmp(big.add(small)); + } + + /** + * Makes this FDBigInteger immutable. + */ + /*@ + @ assignable this.isImmutable; + @ ensures this.isImmutable; + @*/ + public void makeImmutable() { + this.isImmutable = true; + } + + /** + * Multiplies this FDBigInteger by an integer. + * + * @param i The factor by which to multiply this FDBigInteger. + * @return This FDBigInteger multiplied by an integer. + */ + /*@ + @ requires this.value() == 0; + @ assignable \nothing; + @ ensures \result == this; + @ + @ also + @ + @ requires this.value() != 0; + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() * UNSIGNED(i)); + @*/ + private FDBigInteger mult(int i) { + if (this.nWords == 0) { + return this; + } + int[] r = new int[nWords + 1]; + mult(data, nWords, i, r); + return new FDBigInteger(r, offset); + } + + /** + * Multiplies this FDBigInteger by another FDBigInteger. + * + * @param other The FDBigInteger factor by which to multiply. + * @return The product of this and the parameter FDBigIntegers. + */ + /*@ + @ requires this.value() == 0; + @ assignable \nothing; + @ ensures \result == this; + @ + @ also + @ + @ requires this.value() != 0 && other.value() == 0; + @ assignable \nothing; + @ ensures \result == other; + @ + @ also + @ + @ requires this.value() != 0 && other.value() != 0; + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() * other.value()); + @*/ + private FDBigInteger mult(FDBigInteger other) { + if (this.nWords == 0) { + return this; + } + if (this.size() == 1) { + return other.mult(data[0]); + } + if (other.nWords == 0) { + return other; + } + if (other.size() == 1) { + return this.mult(other.data[0]); + } + int[] r = new int[nWords + other.nWords]; + mult(this.data, this.nWords, other.data, other.nWords, r); + return new FDBigInteger(r, this.offset + other.offset); + } + + /** + * Adds another FDBigInteger to this FDBigInteger. + * + * @param other The FDBigInteger to add. + * @return The sum of the FDBigIntegers. + */ + /*@ + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() + other.value()); + @*/ + private FDBigInteger add(FDBigInteger other) { + FDBigInteger big, small; + int bigLen, smallLen; + int tSize = this.size(); + int oSize = other.size(); + if (tSize >= oSize) { + big = this; + bigLen = tSize; + small = other; + smallLen = oSize; + } else { + big = other; + bigLen = oSize; + small = this; + smallLen = tSize; + } + int[] r = new int[bigLen + 1]; + int i = 0; + long carry = 0L; + for (; i < smallLen; i++) { + carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) ) + + ((i < small.offset ? 0L : (small.data[i - small.offset] & LONG_MASK))); + r[i] = (int) carry; + carry >>= 32; // signed shift. + } + for (; i < bigLen; i++) { + carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) ); + r[i] = (int) carry; + carry >>= 32; // signed shift. + } + r[bigLen] = (int) carry; + return new FDBigInteger(r, 0); + } + + + /** + * Multiplies a FDBigInteger by an int and adds another int. The + * result is computed in place. This method is intended only to be invoked + * from + * + * FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits) + * . + * + * @param iv The factor by which to multiply this FDBigInteger. + * @param addend The value to add to the product of this + * FDBigInteger and iv. + */ + /*@ + @ requires this.value()*UNSIGNED(iv) + UNSIGNED(addend) < ((\bigint)1) << ((this.data.length + this.offset)*32); + @ assignable this.data[*]; + @ ensures this.value() == \old(this.value()*UNSIGNED(iv) + UNSIGNED(addend)); + @*/ + private /*@ helper @*/ void multAddMe(int iv, int addend) { + long v = iv & LONG_MASK; + // unroll 0th iteration, doing addition. + long p = v * (data[0] & LONG_MASK) + (addend & LONG_MASK); + data[0] = (int) p; + p >>>= 32; + for (int i = 1; i < nWords; i++) { + p += v * (data[i] & LONG_MASK); + data[i] = (int) p; + p >>>= 32; + } + if (p != 0L) { + data[nWords++] = (int) p; // will fail noisily if illegal! + } + } + + // + // original doc: + // + // do this -=q*S + // returns borrow + // + /** + * Multiplies the parameters and subtracts them from this + * FDBigInteger. + * + * @param q The integer parameter. + * @param S The FDBigInteger parameter. + * @return this - q*S. + */ + /*@ + @ ensures nWords == 0 ==> offset == 0; + @ ensures nWords > 0 ==> data[nWords - 1] != 0; + @*/ + /*@ + @ requires 0 < q && q <= (1L << 31); + @ requires data != null; + @ requires 0 <= nWords && nWords <= data.length && offset >= 0; + @ requires !this.isImmutable; + @ requires this.size() == S.size(); + @ requires this != S; + @ assignable this.nWords, this.offset, this.data, this.data[*]; + @ ensures -q <= \result && \result <= 0; + @ ensures this.size() == \old(this.size()); + @ ensures this.value() + (\result << (this.size()*32)) == \old(this.value() - q*S.value()); + @ ensures this.offset == \old(Math.min(this.offset, S.offset)); + @ ensures \old(this.offset <= S.offset) ==> this.nWords == \old(this.nWords); + @ ensures \old(this.offset <= S.offset) ==> this.offset == \old(this.offset); + @ ensures \old(this.offset <= S.offset) ==> this.data == \old(this.data); + @ + @ also + @ + @ requires q == 0; + @ assignable \nothing; + @ ensures \result == 0; + @*/ + private /*@ helper @*/ long multDiffMe(long q, FDBigInteger S) { + long diff = 0L; + if (q != 0) { + int deltaSize = S.offset - this.offset; + if (deltaSize >= 0) { + int[] sd = S.data; + int[] td = this.data; + for (int sIndex = 0, tIndex = deltaSize; sIndex < S.nWords; sIndex++, tIndex++) { + diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK); + td[tIndex] = (int) diff; + diff >>= 32; // N.B. SIGNED shift. + } + } else { + deltaSize = -deltaSize; + int[] rd = new int[nWords + deltaSize]; + int sIndex = 0; + int rIndex = 0; + int[] sd = S.data; + for (; rIndex < deltaSize && sIndex < S.nWords; sIndex++, rIndex++) { + diff -= q * (sd[sIndex] & LONG_MASK); + rd[rIndex] = (int) diff; + diff >>= 32; // N.B. SIGNED shift. + } + int tIndex = 0; + int[] td = this.data; + for (; sIndex < S.nWords; sIndex++, tIndex++, rIndex++) { + diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK); + rd[rIndex] = (int) diff; + diff >>= 32; // N.B. SIGNED shift. + } + this.nWords += deltaSize; + this.offset -= deltaSize; + this.data = rd; + } + } + return diff; + } + + + /** + * Multiplies by 10 a big integer represented as an array. The final carry + * is returned. + * + * @param src The array representation of the big integer. + * @param srcLen The number of elements of src to use. + * @param dst The product array. + * @return The final carry of the multiplication. + */ + /*@ + @ requires src.length >= srcLen && dst.length >= srcLen; + @ assignable dst[0 .. srcLen - 1]; + @ ensures 0 <= \result && \result < 10; + @ ensures AP(dst, srcLen) + (\result << (srcLen*32)) == \old(AP(src, srcLen) * 10); + @*/ + private static int multAndCarryBy10(int[] src, int srcLen, int[] dst) { + long carry = 0; + for (int i = 0; i < srcLen; i++) { + long product = (src[i] & LONG_MASK) * 10L + carry; + dst[i] = (int) product; + carry = product >>> 32; + } + return (int) carry; + } + + /** + * Multiplies by a constant value a big integer represented as an array. + * The constant factor is an int. + * + * @param src The array representation of the big integer. + * @param srcLen The number of elements of src to use. + * @param value The constant factor by which to multiply. + * @param dst The product array. + */ + /*@ + @ requires src.length >= srcLen && dst.length >= srcLen + 1; + @ assignable dst[0 .. srcLen]; + @ ensures AP(dst, srcLen + 1) == \old(AP(src, srcLen) * UNSIGNED(value)); + @*/ + private static void mult(int[] src, int srcLen, int value, int[] dst) { + long val = value & LONG_MASK; + long carry = 0; + for (int i = 0; i < srcLen; i++) { + long product = (src[i] & LONG_MASK) * val + carry; + dst[i] = (int) product; + carry = product >>> 32; + } + dst[srcLen] = (int) carry; + } + + /** + * Multiplies by a constant value a big integer represented as an array. + * The constant factor is a long represent as two ints. + * + * @param src The array representation of the big integer. + * @param srcLen The number of elements of src to use. + * @param v0 The lower 32 bits of the long factor. + * @param v1 The upper 32 bits of the long factor. + * @param dst The product array. + */ + /*@ + @ requires src != dst; + @ requires src.length >= srcLen && dst.length >= srcLen + 2; + @ assignable dst[0 .. srcLen + 1]; + @ ensures AP(dst, srcLen + 2) == \old(AP(src, srcLen) * (UNSIGNED(v0) + (UNSIGNED(v1) << 32))); + @*/ + private static void mult(int[] src, int srcLen, int v0, int v1, int[] dst) { + long v = v0 & LONG_MASK; + long carry = 0; + for (int j = 0; j < srcLen; j++) { + long product = v * (src[j] & LONG_MASK) + carry; + dst[j] = (int) product; + carry = product >>> 32; + } + dst[srcLen] = (int) carry; + v = v1 & LONG_MASK; + carry = 0; + for (int j = 0; j < srcLen; j++) { + long product = (dst[j + 1] & LONG_MASK) + v * (src[j] & LONG_MASK) + carry; + dst[j + 1] = (int) product; + carry = product >>> 32; + } + dst[srcLen + 1] = (int) carry; + } + + // Fails assertion for negative exponent. + /** + * Computes 5 raised to a given power. + * + * @param p The exponent of 5. + * @return 5p. + */ + private static FDBigInteger big5pow(int p) { + assert p >= 0 : p; // negative power of 5 + if (p < MAX_FIVE_POW) { + return POW_5_CACHE[p]; + } + return big5powRec(p); + } + + // slow path + /** + * Computes 5 raised to a given power. + * + * @param p The exponent of 5. + * @return 5p. + */ + private static FDBigInteger big5powRec(int p) { + if (p < MAX_FIVE_POW) { + return POW_5_CACHE[p]; + } + // construct the value. + // recursively. + int q, r; + // in order to compute 5^p, + // compute its square root, 5^(p/2) and square. + // or, let q = p / 2, r = p -q, then + // 5^p = 5^(q+r) = 5^q * 5^r + q = p >> 1; + r = p - q; + FDBigInteger bigq = big5powRec(q); + if (r < SMALL_5_POW.length) { + return bigq.mult(SMALL_5_POW[r]); + } else { + return bigq.mult(big5powRec(r)); + } + } + + // for debugging ... + /** + * Converts this FDBigInteger to a hexadecimal string. + * + * @return The hexadecimal string representation. + */ + public String toHexString(){ + if(nWords ==0) { + return "0"; + } + StringBuilder sb = new StringBuilder((nWords +offset)*8); + for(int i= nWords -1; i>=0; i--) { + String subStr = Integer.toHexString(data[i]); + for(int j = subStr.length(); j<8; j++) { + sb.append('0'); + } + sb.append(subStr); + } + for(int i=offset; i>0; i--) { + sb.append("00000000"); + } + return sb.toString(); + } + + // for debugging ... + /** + * Converts this FDBigInteger to a BigInteger. + * + * @return The BigInteger representation. + */ + public BigInteger toBigInteger() { + byte[] magnitude = new byte[nWords * 4 + 1]; + for (int i = 0; i < nWords; i++) { + int w = data[i]; + magnitude[magnitude.length - 4 * i - 1] = (byte) w; + magnitude[magnitude.length - 4 * i - 2] = (byte) (w >> 8); + magnitude[magnitude.length - 4 * i - 3] = (byte) (w >> 16); + magnitude[magnitude.length - 4 * i - 4] = (byte) (w >> 24); + } + return new BigInteger(magnitude).shiftLeft(offset * 32); + } + + // for debugging ... + /** + * Converts this FDBigInteger to a string. + * + * @return The string representation. + */ + @Override + public String toString(){ + return toBigInteger().toString(); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/jdk/internal/math/FloatConsts.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/math/FloatConsts.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.math; + +/** + * This class contains additional constants documenting limits of the + * float type. + * + * @author Joseph D. Darcy + */ + +public class FloatConsts { + /** + * Don't let anyone instantiate this class. + */ + private FloatConsts() {} + + public static final float POSITIVE_INFINITY = java.lang.Float.POSITIVE_INFINITY; + public static final float NEGATIVE_INFINITY = java.lang.Float.NEGATIVE_INFINITY; + public static final float NaN = java.lang.Float.NaN; + public static final float MAX_VALUE = java.lang.Float.MAX_VALUE; + public static final float MIN_VALUE = java.lang.Float.MIN_VALUE; + + /** + * A constant holding the smallest positive normal value of type + * float, 2-126. It is equal to the value + * returned by Float.intBitsToFloat(0x00800000). + */ + public static final float MIN_NORMAL = 1.17549435E-38f; + + /** + * The number of logical bits in the significand of a + * float number, including the implicit bit. + */ + public static final int SIGNIFICAND_WIDTH = 24; + + /** + * Maximum exponent a finite float number may have. + * It is equal to the value returned by + * Math.ilogb(Float.MAX_VALUE). + */ + public static final int MAX_EXPONENT = 127; + + /** + * Minimum exponent a normalized float number may + * have. It is equal to the value returned by + * Math.ilogb(Float.MIN_NORMAL). + */ + public static final int MIN_EXPONENT = -126; + + /** + * The exponent the smallest positive float subnormal + * value would have if it could be normalized. + */ + public static final int MIN_SUB_EXPONENT = MIN_EXPONENT - + (SIGNIFICAND_WIDTH - 1); + + /** + * Bias used in representing a float exponent. + */ + public static final int EXP_BIAS = 127; + + /** + * Bit mask to isolate the sign bit of a float. + */ + public static final int SIGN_BIT_MASK = 0x80000000; + + /** + * Bit mask to isolate the exponent field of a + * float. + */ + public static final int EXP_BIT_MASK = 0x7F800000; + + /** + * Bit mask to isolate the significand field of a + * float. + */ + public static final int SIGNIF_BIT_MASK = 0x007FFFFF; + + static { + // verify bit masks cover all bit positions and that the bit + // masks are non-overlapping + assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0) && + (((SIGN_BIT_MASK & EXP_BIT_MASK) == 0) && + ((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0) && + ((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0))); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,2552 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.math; + +import java.util.Arrays; +import java.util.regex.*; + +/** + * A class for converting between ASCII and decimal representations of a single + * or double precision floating point number. Most conversions are provided via + * static convenience methods, although a BinaryToASCIIConverter + * instance may be obtained and reused. + */ +public class FloatingDecimal{ + // + // Constants of the implementation; + // most are IEEE-754 related. + // (There are more really boring constants at the end.) + // + static final int EXP_SHIFT = DoubleConsts.SIGNIFICAND_WIDTH - 1; + static final long FRACT_HOB = ( 1L<String. + * + * @param d The double precision value. + * @return The value converted to a String. + */ + public static String toJavaFormatString(double d) { + return getBinaryToASCIIConverter(d).toJavaFormatString(); + } + + /** + * Converts a single precision floating point value to a String. + * + * @param f The single precision value. + * @return The value converted to a String. + */ + public static String toJavaFormatString(float f) { + return getBinaryToASCIIConverter(f).toJavaFormatString(); + } + + /** + * Appends a double precision floating point value to an Appendable. + * @param d The double precision value. + * @param buf The Appendable with the value appended. + */ + public static void appendTo(double d, Appendable buf) { + getBinaryToASCIIConverter(d).appendTo(buf); + } + + /** + * Appends a single precision floating point value to an Appendable. + * @param f The single precision value. + * @param buf The Appendable with the value appended. + */ + public static void appendTo(float f, Appendable buf) { + getBinaryToASCIIConverter(f).appendTo(buf); + } + + /** + * Converts a String to a double precision floating point value. + * + * @param s The String to convert. + * @return The double precision value. + * @throws NumberFormatException If the String does not + * represent a properly formatted double precision value. + */ + public static double parseDouble(String s) throws NumberFormatException { + return readJavaFormatString(s).doubleValue(); + } + + /** + * Converts a String to a single precision floating point value. + * + * @param s The String to convert. + * @return The single precision value. + * @throws NumberFormatException If the String does not + * represent a properly formatted single precision value. + */ + public static float parseFloat(String s) throws NumberFormatException { + return readJavaFormatString(s).floatValue(); + } + + /** + * A converter which can process single or double precision floating point + * values into an ASCII String representation. + */ + public interface BinaryToASCIIConverter { + /** + * Converts a floating point value into an ASCII String. + * @return The value converted to a String. + */ + public String toJavaFormatString(); + + /** + * Appends a floating point value to an Appendable. + * @param buf The Appendable to receive the value. + */ + public void appendTo(Appendable buf); + + /** + * Retrieves the decimal exponent most closely corresponding to this value. + * @return The decimal exponent. + */ + public int getDecimalExponent(); + + /** + * Retrieves the value as an array of digits. + * @param digits The digit array. + * @return The number of valid digits copied into the array. + */ + public int getDigits(char[] digits); + + /** + * Indicates the sign of the value. + * @return {@code value < 0.0}. + */ + public boolean isNegative(); + + /** + * Indicates whether the value is either infinite or not a number. + * + * @return true if and only if the value is NaN + * or infinite. + */ + public boolean isExceptional(); + + /** + * Indicates whether the value was rounded up during the binary to ASCII + * conversion. + * + * @return true if and only if the value was rounded up. + */ + public boolean digitsRoundedUp(); + + /** + * Indicates whether the binary to ASCII conversion was exact. + * + * @return true if any only if the conversion was exact. + */ + public boolean decimalDigitsExact(); + } + + /** + * A BinaryToASCIIConverter which represents NaN + * and infinite values. + */ + private static class ExceptionalBinaryToASCIIBuffer implements BinaryToASCIIConverter { + private final String image; + private boolean isNegative; + + public ExceptionalBinaryToASCIIBuffer(String image, boolean isNegative) { + this.image = image; + this.isNegative = isNegative; + } + + @Override + public String toJavaFormatString() { + return image; + } + + @Override + public void appendTo(Appendable buf) { + if (buf instanceof StringBuilder) { + ((StringBuilder) buf).append(image); + } else if (buf instanceof StringBuffer) { + ((StringBuffer) buf).append(image); + } else { + assert false; + } + } + + @Override + public int getDecimalExponent() { + throw new IllegalArgumentException("Exceptional value does not have an exponent"); + } + + @Override + public int getDigits(char[] digits) { + throw new IllegalArgumentException("Exceptional value does not have digits"); + } + + @Override + public boolean isNegative() { + return isNegative; + } + + @Override + public boolean isExceptional() { + return true; + } + + @Override + public boolean digitsRoundedUp() { + throw new IllegalArgumentException("Exceptional value is not rounded"); + } + + @Override + public boolean decimalDigitsExact() { + throw new IllegalArgumentException("Exceptional value is not exact"); + } + } + + private static final String INFINITY_REP = "Infinity"; + private static final int INFINITY_LENGTH = INFINITY_REP.length(); + private static final String NAN_REP = "NaN"; + private static final int NAN_LENGTH = NAN_REP.length(); + + private static final BinaryToASCIIConverter B2AC_POSITIVE_INFINITY = new ExceptionalBinaryToASCIIBuffer(INFINITY_REP, false); + private static final BinaryToASCIIConverter B2AC_NEGATIVE_INFINITY = new ExceptionalBinaryToASCIIBuffer("-" + INFINITY_REP, true); + private static final BinaryToASCIIConverter B2AC_NOT_A_NUMBER = new ExceptionalBinaryToASCIIBuffer(NAN_REP, false); + private static final BinaryToASCIIConverter B2AC_POSITIVE_ZERO = new BinaryToASCIIBuffer(false, new char[]{'0'}); + private static final BinaryToASCIIConverter B2AC_NEGATIVE_ZERO = new BinaryToASCIIBuffer(true, new char[]{'0'}); + + /** + * A buffered implementation of BinaryToASCIIConverter. + */ + static class BinaryToASCIIBuffer implements BinaryToASCIIConverter { + private boolean isNegative; + private int decExponent; + private int firstDigitIndex; + private int nDigits; + private final char[] digits; + private final char[] buffer = new char[26]; + + // + // The fields below provide additional information about the result of + // the binary to decimal digits conversion done in dtoa() and roundup() + // methods. They are changed if needed by those two methods. + // + + // True if the dtoa() binary to decimal conversion was exact. + private boolean exactDecimalConversion = false; + + // True if the result of the binary to decimal conversion was rounded-up + // at the end of the conversion process, i.e. roundUp() method was called. + private boolean decimalDigitsRoundedUp = false; + + /** + * Default constructor; used for non-zero values, + * BinaryToASCIIBuffer may be thread-local and reused + */ + BinaryToASCIIBuffer(){ + this.digits = new char[20]; + } + + /** + * Creates a specialized value (positive and negative zeros). + */ + BinaryToASCIIBuffer(boolean isNegative, char[] digits){ + this.isNegative = isNegative; + this.decExponent = 0; + this.digits = digits; + this.firstDigitIndex = 0; + this.nDigits = digits.length; + } + + @Override + public String toJavaFormatString() { + int len = getChars(buffer); + return new String(buffer, 0, len); + } + + @Override + public void appendTo(Appendable buf) { + int len = getChars(buffer); + if (buf instanceof StringBuilder) { + ((StringBuilder) buf).append(buffer, 0, len); + } else if (buf instanceof StringBuffer) { + ((StringBuffer) buf).append(buffer, 0, len); + } else { + assert false; + } + } + + @Override + public int getDecimalExponent() { + return decExponent; + } + + @Override + public int getDigits(char[] digits) { + System.arraycopy(this.digits,firstDigitIndex,digits,0,this.nDigits); + return this.nDigits; + } + + @Override + public boolean isNegative() { + return isNegative; + } + + @Override + public boolean isExceptional() { + return false; + } + + @Override + public boolean digitsRoundedUp() { + return decimalDigitsRoundedUp; + } + + @Override + public boolean decimalDigitsExact() { + return exactDecimalConversion; + } + + private void setSign(boolean isNegative) { + this.isNegative = isNegative; + } + + /** + * This is the easy subcase -- + * all the significant bits, after scaling, are held in lvalue. + * negSign and decExponent tell us what processing and scaling + * has already been done. Exceptional cases have already been + * stripped out. + * In particular: + * lvalue is a finite number (not Inf, nor NaN) + * lvalue > 0L (not zero, nor negative). + * + * The only reason that we develop the digits here, rather than + * calling on Long.toString() is that we can do it a little faster, + * and besides want to treat trailing 0s specially. If Long.toString + * changes, we should re-evaluate this strategy! + */ + private void developLongDigits( int decExponent, long lvalue, int insignificantDigits ){ + if ( insignificantDigits != 0 ){ + // Discard non-significant low-order bits, while rounding, + // up to insignificant value. + long pow10 = FDBigInteger.LONG_5_POW[insignificantDigits] << insignificantDigits; // 10^i == 5^i * 2^i; + long residue = lvalue % pow10; + lvalue /= pow10; + decExponent += insignificantDigits; + if ( residue >= (pow10>>1) ){ + // round up based on the low-order bits we're discarding + lvalue++; + } + } + int digitno = digits.length -1; + int c; + if ( lvalue <= Integer.MAX_VALUE ){ + assert lvalue > 0L : lvalue; // lvalue <= 0 + // even easier subcase! + // can do int arithmetic rather than long! + int ivalue = (int)lvalue; + c = ivalue%10; + ivalue /= 10; + while ( c == 0 ){ + decExponent++; + c = ivalue%10; + ivalue /= 10; + } + while ( ivalue != 0){ + digits[digitno--] = (char)(c+'0'); + decExponent++; + c = ivalue%10; + ivalue /= 10; + } + digits[digitno] = (char)(c+'0'); + } else { + // same algorithm as above (same bugs, too ) + // but using long arithmetic. + c = (int)(lvalue%10L); + lvalue /= 10L; + while ( c == 0 ){ + decExponent++; + c = (int)(lvalue%10L); + lvalue /= 10L; + } + while ( lvalue != 0L ){ + digits[digitno--] = (char)(c+'0'); + decExponent++; + c = (int)(lvalue%10L); + lvalue /= 10; + } + digits[digitno] = (char)(c+'0'); + } + this.decExponent = decExponent+1; + this.firstDigitIndex = digitno; + this.nDigits = this.digits.length - digitno; + } + + private void dtoa( int binExp, long fractBits, int nSignificantBits, boolean isCompatibleFormat) + { + assert fractBits > 0 ; // fractBits here can't be zero or negative + assert (fractBits & FRACT_HOB)!=0 ; // Hi-order bit should be set + // Examine number. Determine if it is an easy case, + // which we can do pretty trivially using float/long conversion, + // or whether we must do real work. + final int tailZeros = Long.numberOfTrailingZeros(fractBits); + + // number of significant bits of fractBits; + final int nFractBits = EXP_SHIFT+1-tailZeros; + + // reset flags to default values as dtoa() does not always set these + // flags and a prior call to dtoa() might have set them to incorrect + // values with respect to the current state. + decimalDigitsRoundedUp = false; + exactDecimalConversion = false; + + // number of significant bits to the right of the point. + int nTinyBits = Math.max( 0, nFractBits - binExp - 1 ); + if ( binExp <= MAX_SMALL_BIN_EXP && binExp >= MIN_SMALL_BIN_EXP ){ + // Look more closely at the number to decide if, + // with scaling by 10^nTinyBits, the result will fit in + // a long. + if ( (nTinyBits < FDBigInteger.LONG_5_POW.length) && ((nFractBits + N_5_BITS[nTinyBits]) < 64 ) ){ + // + // We can do this: + // take the fraction bits, which are normalized. + // (a) nTinyBits == 0: Shift left or right appropriately + // to align the binary point at the extreme right, i.e. + // where a long int point is expected to be. The integer + // result is easily converted to a string. + // (b) nTinyBits > 0: Shift right by EXP_SHIFT-nFractBits, + // which effectively converts to long and scales by + // 2^nTinyBits. Then multiply by 5^nTinyBits to + // complete the scaling. We know this won't overflow + // because we just counted the number of bits necessary + // in the result. The integer you get from this can + // then be converted to a string pretty easily. + // + if ( nTinyBits == 0 ) { + int insignificant; + if ( binExp > nSignificantBits ){ + insignificant = insignificantDigitsForPow2(binExp-nSignificantBits-1); + } else { + insignificant = 0; + } + if ( binExp >= EXP_SHIFT ){ + fractBits <<= (binExp-EXP_SHIFT); + } else { + fractBits >>>= (EXP_SHIFT-binExp) ; + } + developLongDigits( 0, fractBits, insignificant ); + return; + } + // + // The following causes excess digits to be printed + // out in the single-float case. Our manipulation of + // halfULP here is apparently not correct. If we + // better understand how this works, perhaps we can + // use this special case again. But for the time being, + // we do not. + // else { + // fractBits >>>= EXP_SHIFT+1-nFractBits; + // fractBits//= long5pow[ nTinyBits ]; + // halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits); + // developLongDigits( -nTinyBits, fractBits, insignificantDigits(halfULP) ); + // return; + // } + // + } + } + // + // This is the hard case. We are going to compute large positive + // integers B and S and integer decExp, s.t. + // d = ( B / S )// 10^decExp + // 1 <= B / S < 10 + // Obvious choices are: + // decExp = floor( log10(d) ) + // B = d// 2^nTinyBits// 10^max( 0, -decExp ) + // S = 10^max( 0, decExp)// 2^nTinyBits + // (noting that nTinyBits has already been forced to non-negative) + // I am also going to compute a large positive integer + // M = (1/2^nSignificantBits)// 2^nTinyBits// 10^max( 0, -decExp ) + // i.e. M is (1/2) of the ULP of d, scaled like B. + // When we iterate through dividing B/S and picking off the + // quotient bits, we will know when to stop when the remainder + // is <= M. + // + // We keep track of powers of 2 and powers of 5. + // + int decExp = estimateDecExp(fractBits,binExp); + int B2, B5; // powers of 2 and powers of 5, respectively, in B + int S2, S5; // powers of 2 and powers of 5, respectively, in S + int M2, M5; // powers of 2 and powers of 5, respectively, in M + + B5 = Math.max( 0, -decExp ); + B2 = B5 + nTinyBits + binExp; + + S5 = Math.max( 0, decExp ); + S2 = S5 + nTinyBits; + + M5 = B5; + M2 = B2 - nSignificantBits; + + // + // the long integer fractBits contains the (nFractBits) interesting + // bits from the mantissa of d ( hidden 1 added if necessary) followed + // by (EXP_SHIFT+1-nFractBits) zeros. In the interest of compactness, + // I will shift out those zeros before turning fractBits into a + // FDBigInteger. The resulting whole number will be + // d * 2^(nFractBits-1-binExp). + // + fractBits >>>= tailZeros; + B2 -= nFractBits-1; + int common2factor = Math.min( B2, S2 ); + B2 -= common2factor; + S2 -= common2factor; + M2 -= common2factor; + + // + // HACK!! For exact powers of two, the next smallest number + // is only half as far away as we think (because the meaning of + // ULP changes at power-of-two bounds) for this reason, we + // hack M2. Hope this works. + // + if ( nFractBits == 1 ) { + M2 -= 1; + } + + if ( M2 < 0 ){ + // oops. + // since we cannot scale M down far enough, + // we must scale the other values up. + B2 -= M2; + S2 -= M2; + M2 = 0; + } + // + // Construct, Scale, iterate. + // Some day, we'll write a stopping test that takes + // account of the asymmetry of the spacing of floating-point + // numbers below perfect powers of 2 + // 26 Sept 96 is not that day. + // So we use a symmetric test. + // + int ndigit = 0; + boolean low, high; + long lowDigitDifference; + int q; + + // + // Detect the special cases where all the numbers we are about + // to compute will fit in int or long integers. + // In these cases, we will avoid doing FDBigInteger arithmetic. + // We use the same algorithms, except that we "normalize" + // our FDBigIntegers before iterating. This is to make division easier, + // as it makes our fist guess (quotient of high-order words) + // more accurate! + // + // Some day, we'll write a stopping test that takes + // account of the asymmetry of the spacing of floating-point + // numbers below perfect powers of 2 + // 26 Sept 96 is not that day. + // So we use a symmetric test. + // + // binary digits needed to represent B, approx. + int Bbits = nFractBits + B2 + (( B5 < N_5_BITS.length )? N_5_BITS[B5] : ( B5*3 )); + + // binary digits needed to represent 10*S, approx. + int tenSbits = S2+1 + (( (S5+1) < N_5_BITS.length )? N_5_BITS[(S5+1)] : ( (S5+1)*3 )); + if ( Bbits < 64 && tenSbits < 64){ + if ( Bbits < 32 && tenSbits < 32){ + // wa-hoo! They're all ints! + int b = ((int)fractBits * FDBigInteger.SMALL_5_POW[B5] ) << B2; + int s = FDBigInteger.SMALL_5_POW[S5] << S2; + int m = FDBigInteger.SMALL_5_POW[M5] << M2; + int tens = s * 10; + // + // Unroll the first iteration. If our decExp estimate + // was too high, our first quotient will be zero. In this + // case, we discard it and decrement decExp. + // + ndigit = 0; + q = b / s; + b = 10 * ( b % s ); + m *= 10; + low = (b < m ); + high = (b+m > tens ); + assert q < 10 : q; // excessively large digit + if ( (q == 0) && ! high ){ + // oops. Usually ignore leading zero. + decExp--; + } else { + digits[ndigit++] = (char)('0' + q); + } + // + // HACK! Java spec sez that we always have at least + // one digit after the . in either F- or E-form output. + // Thus we will need more than one digit if we're using + // E-form + // + if ( !isCompatibleFormat ||decExp < -3 || decExp >= 8 ){ + high = low = false; + } + while( ! low && ! high ){ + q = b / s; + b = 10 * ( b % s ); + m *= 10; + assert q < 10 : q; // excessively large digit + if ( m > 0L ){ + low = (b < m ); + high = (b+m > tens ); + } else { + // hack -- m might overflow! + // in this case, it is certainly > b, + // which won't + // and b+m > tens, too, since that has overflowed + // either! + low = true; + high = true; + } + digits[ndigit++] = (char)('0' + q); + } + lowDigitDifference = (b<<1) - tens; + exactDecimalConversion = (b == 0); + } else { + // still good! they're all longs! + long b = (fractBits * FDBigInteger.LONG_5_POW[B5] ) << B2; + long s = FDBigInteger.LONG_5_POW[S5] << S2; + long m = FDBigInteger.LONG_5_POW[M5] << M2; + long tens = s * 10L; + // + // Unroll the first iteration. If our decExp estimate + // was too high, our first quotient will be zero. In this + // case, we discard it and decrement decExp. + // + ndigit = 0; + q = (int) ( b / s ); + b = 10L * ( b % s ); + m *= 10L; + low = (b < m ); + high = (b+m > tens ); + assert q < 10 : q; // excessively large digit + if ( (q == 0) && ! high ){ + // oops. Usually ignore leading zero. + decExp--; + } else { + digits[ndigit++] = (char)('0' + q); + } + // + // HACK! Java spec sez that we always have at least + // one digit after the . in either F- or E-form output. + // Thus we will need more than one digit if we're using + // E-form + // + if ( !isCompatibleFormat || decExp < -3 || decExp >= 8 ){ + high = low = false; + } + while( ! low && ! high ){ + q = (int) ( b / s ); + b = 10 * ( b % s ); + m *= 10; + assert q < 10 : q; // excessively large digit + if ( m > 0L ){ + low = (b < m ); + high = (b+m > tens ); + } else { + // hack -- m might overflow! + // in this case, it is certainly > b, + // which won't + // and b+m > tens, too, since that has overflowed + // either! + low = true; + high = true; + } + digits[ndigit++] = (char)('0' + q); + } + lowDigitDifference = (b<<1) - tens; + exactDecimalConversion = (b == 0); + } + } else { + // + // We really must do FDBigInteger arithmetic. + // Fist, construct our FDBigInteger initial values. + // + FDBigInteger Sval = FDBigInteger.valueOfPow52(S5, S2); + int shiftBias = Sval.getNormalizationBias(); + Sval = Sval.leftShift(shiftBias); // normalize so that division works better + + FDBigInteger Bval = FDBigInteger.valueOfMulPow52(fractBits, B5, B2 + shiftBias); + FDBigInteger Mval = FDBigInteger.valueOfPow52(M5 + 1, M2 + shiftBias + 1); + + FDBigInteger tenSval = FDBigInteger.valueOfPow52(S5 + 1, S2 + shiftBias + 1); //Sval.mult( 10 ); + // + // Unroll the first iteration. If our decExp estimate + // was too high, our first quotient will be zero. In this + // case, we discard it and decrement decExp. + // + ndigit = 0; + q = Bval.quoRemIteration( Sval ); + low = (Bval.cmp( Mval ) < 0); + high = tenSval.addAndCmp(Bval,Mval)<=0; + + assert q < 10 : q; // excessively large digit + if ( (q == 0) && ! high ){ + // oops. Usually ignore leading zero. + decExp--; + } else { + digits[ndigit++] = (char)('0' + q); + } + // + // HACK! Java spec sez that we always have at least + // one digit after the . in either F- or E-form output. + // Thus we will need more than one digit if we're using + // E-form + // + if (!isCompatibleFormat || decExp < -3 || decExp >= 8 ){ + high = low = false; + } + while( ! low && ! high ){ + q = Bval.quoRemIteration( Sval ); + assert q < 10 : q; // excessively large digit + Mval = Mval.multBy10(); //Mval = Mval.mult( 10 ); + low = (Bval.cmp( Mval ) < 0); + high = tenSval.addAndCmp(Bval,Mval)<=0; + digits[ndigit++] = (char)('0' + q); + } + if ( high && low ){ + Bval = Bval.leftShift(1); + lowDigitDifference = Bval.cmp(tenSval); + } else { + lowDigitDifference = 0L; // this here only for flow analysis! + } + exactDecimalConversion = (Bval.cmp( FDBigInteger.ZERO ) == 0); + } + this.decExponent = decExp+1; + this.firstDigitIndex = 0; + this.nDigits = ndigit; + // + // Last digit gets rounded based on stopping condition. + // + if ( high ){ + if ( low ){ + if ( lowDigitDifference == 0L ){ + // it's a tie! + // choose based on which digits we like. + if ( (digits[firstDigitIndex+nDigits-1]&1) != 0 ) { + roundup(); + } + } else if ( lowDigitDifference > 0 ){ + roundup(); + } + } else { + roundup(); + } + } + } + + // add one to the least significant digit. + // in the unlikely event there is a carry out, deal with it. + // assert that this will only happen where there + // is only one digit, e.g. (float)1e-44 seems to do it. + // + private void roundup() { + int i = (firstDigitIndex + nDigits - 1); + int q = digits[i]; + if (q == '9') { + while (q == '9' && i > firstDigitIndex) { + digits[i] = '0'; + q = digits[--i]; + } + if (q == '9') { + // carryout! High-order 1, rest 0s, larger exp. + decExponent += 1; + digits[firstDigitIndex] = '1'; + return; + } + // else fall through. + } + digits[i] = (char) (q + 1); + decimalDigitsRoundedUp = true; + } + + /** + * Estimate decimal exponent. (If it is small-ish, + * we could double-check.) + * + * First, scale the mantissa bits such that 1 <= d2 < 2. + * We are then going to estimate + * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5) + * and so we can estimate + * log10(d) ~=~ log10(d2) + binExp * log10(2) + * take the floor and call it decExp. + */ + static int estimateDecExp(long fractBits, int binExp) { + double d2 = Double.longBitsToDouble( EXP_ONE | ( fractBits & DoubleConsts.SIGNIF_BIT_MASK ) ); + double d = (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981; + long dBits = Double.doubleToRawLongBits(d); //can't be NaN here so use raw + int exponent = (int)((dBits & DoubleConsts.EXP_BIT_MASK) >> EXP_SHIFT) - DoubleConsts.EXP_BIAS; + boolean isNegative = (dBits & DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign + if(exponent>=0 && exponent<52) { // hot path + long mask = DoubleConsts.SIGNIF_BIT_MASK >> exponent; + int r = (int)(( (dBits&DoubleConsts.SIGNIF_BIT_MASK) | FRACT_HOB )>>(EXP_SHIFT-exponent)); + return isNegative ? (((mask & dBits) == 0L ) ? -r : -r-1 ) : r; + } else if (exponent < 0) { + return (((dBits&~DoubleConsts.SIGN_BIT_MASK) == 0) ? 0 : + ( (isNegative) ? -1 : 0) ); + } else { //if (exponent >= 52) + return (int)d; + } + } + + private static int insignificantDigits(int insignificant) { + int i; + for ( i = 0; insignificant >= 10L; i++ ) { + insignificant /= 10L; + } + return i; + } + + /** + * Calculates + *
    +         * insignificantDigitsForPow2(v) == insignificantDigits(1L<
    +         */
    +        private static int insignificantDigitsForPow2(int p2) {
    +            if(p2>1 && p2 < insignificantDigitsNumber.length) {
    +                return insignificantDigitsNumber[p2];
    +            }
    +            return 0;
    +        }
    +
    +        /**
    +         *  If insignificant==(1L << ixd)
    +         *  i = insignificantDigitsNumber[idx] is the same as:
    +         *  int i;
    +         *  for ( i = 0; insignificant >= 10L; i++ )
    +         *         insignificant /= 10L;
    +         */
    +        private static int[] insignificantDigitsNumber = {
    +            0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3,
    +            4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
    +            8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11,
    +            12, 12, 12, 12, 13, 13, 13, 14, 14, 14,
    +            15, 15, 15, 15, 16, 16, 16, 17, 17, 17,
    +            18, 18, 18, 19
    +        };
    +
    +        // approximately ceil( log2( long5pow[i] ) )
    +        private static final int[] N_5_BITS = {
    +                0,
    +                3,
    +                5,
    +                7,
    +                10,
    +                12,
    +                14,
    +                17,
    +                19,
    +                21,
    +                24,
    +                26,
    +                28,
    +                31,
    +                33,
    +                35,
    +                38,
    +                40,
    +                42,
    +                45,
    +                47,
    +                49,
    +                52,
    +                54,
    +                56,
    +                59,
    +                61,
    +        };
    +
    +        private int getChars(char[] result) {
    +            assert nDigits <= 19 : nDigits; // generous bound on size of nDigits
    +            int i = 0;
    +            if (isNegative) {
    +                result[0] = '-';
    +                i = 1;
    +            }
    +            if (decExponent > 0 && decExponent < 8) {
    +                // print digits.digits.
    +                int charLength = Math.min(nDigits, decExponent);
    +                System.arraycopy(digits, firstDigitIndex, result, i, charLength);
    +                i += charLength;
    +                if (charLength < decExponent) {
    +                    charLength = decExponent - charLength;
    +                    Arrays.fill(result,i,i+charLength,'0');
    +                    i += charLength;
    +                    result[i++] = '.';
    +                    result[i++] = '0';
    +                } else {
    +                    result[i++] = '.';
    +                    if (charLength < nDigits) {
    +                        int t = nDigits - charLength;
    +                        System.arraycopy(digits, firstDigitIndex+charLength, result, i, t);
    +                        i += t;
    +                    } else {
    +                        result[i++] = '0';
    +                    }
    +                }
    +            } else if (decExponent <= 0 && decExponent > -3) {
    +                result[i++] = '0';
    +                result[i++] = '.';
    +                if (decExponent != 0) {
    +                    Arrays.fill(result, i, i-decExponent, '0');
    +                    i -= decExponent;
    +                }
    +                System.arraycopy(digits, firstDigitIndex, result, i, nDigits);
    +                i += nDigits;
    +            } else {
    +                result[i++] = digits[firstDigitIndex];
    +                result[i++] = '.';
    +                if (nDigits > 1) {
    +                    System.arraycopy(digits, firstDigitIndex+1, result, i, nDigits - 1);
    +                    i += nDigits - 1;
    +                } else {
    +                    result[i++] = '0';
    +                }
    +                result[i++] = 'E';
    +                int e;
    +                if (decExponent <= 0) {
    +                    result[i++] = '-';
    +                    e = -decExponent + 1;
    +                } else {
    +                    e = decExponent - 1;
    +                }
    +                // decExponent has 1, 2, or 3, digits
    +                if (e <= 9) {
    +                    result[i++] = (char) (e + '0');
    +                } else if (e <= 99) {
    +                    result[i++] = (char) (e / 10 + '0');
    +                    result[i++] = (char) (e % 10 + '0');
    +                } else {
    +                    result[i++] = (char) (e / 100 + '0');
    +                    e %= 100;
    +                    result[i++] = (char) (e / 10 + '0');
    +                    result[i++] = (char) (e % 10 + '0');
    +                }
    +            }
    +            return i;
    +        }
    +
    +    }
    +
    +    private static final ThreadLocal threadLocalBinaryToASCIIBuffer =
    +            new ThreadLocal() {
    +                @Override
    +                protected BinaryToASCIIBuffer initialValue() {
    +                    return new BinaryToASCIIBuffer();
    +                }
    +            };
    +
    +    private static BinaryToASCIIBuffer getBinaryToASCIIBuffer() {
    +        return threadLocalBinaryToASCIIBuffer.get();
    +    }
    +
    +    /**
    +     * A converter which can process an ASCII String representation
    +     * of a single or double precision floating point value into a
    +     * float or a double.
    +     */
    +    interface ASCIIToBinaryConverter {
    +
    +        double doubleValue();
    +
    +        float floatValue();
    +
    +    }
    +
    +    /**
    +     * A ASCIIToBinaryConverter container for a double.
    +     */
    +    static class PreparedASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
    +        private final double doubleVal;
    +        private final float floatVal;
    +
    +        public PreparedASCIIToBinaryBuffer(double doubleVal, float floatVal) {
    +            this.doubleVal = doubleVal;
    +            this.floatVal = floatVal;
    +        }
    +
    +        @Override
    +        public double doubleValue() {
    +            return doubleVal;
    +        }
    +
    +        @Override
    +        public float floatValue() {
    +            return floatVal;
    +        }
    +    }
    +
    +    static final ASCIIToBinaryConverter A2BC_POSITIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
    +    static final ASCIIToBinaryConverter A2BC_NEGATIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
    +    static final ASCIIToBinaryConverter A2BC_NOT_A_NUMBER  = new PreparedASCIIToBinaryBuffer(Double.NaN, Float.NaN);
    +    static final ASCIIToBinaryConverter A2BC_POSITIVE_ZERO = new PreparedASCIIToBinaryBuffer(0.0d, 0.0f);
    +    static final ASCIIToBinaryConverter A2BC_NEGATIVE_ZERO = new PreparedASCIIToBinaryBuffer(-0.0d, -0.0f);
    +
    +    /**
    +     * A buffered implementation of ASCIIToBinaryConverter.
    +     */
    +    static class ASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
    +        boolean     isNegative;
    +        int         decExponent;
    +        char        digits[];
    +        int         nDigits;
    +
    +        ASCIIToBinaryBuffer( boolean negSign, int decExponent, char[] digits, int n)
    +        {
    +            this.isNegative = negSign;
    +            this.decExponent = decExponent;
    +            this.digits = digits;
    +            this.nDigits = n;
    +        }
    +
    +        /**
    +         * Takes a FloatingDecimal, which we presumably just scanned in,
    +         * and finds out what its value is, as a double.
    +         *
    +         * AS A SIDE EFFECT, SET roundDir TO INDICATE PREFERRED
    +         * ROUNDING DIRECTION in case the result is really destined
    +         * for a single-precision float.
    +         */
    +        @Override
    +        public double doubleValue() {
    +            int kDigits = Math.min(nDigits, MAX_DECIMAL_DIGITS + 1);
    +            //
    +            // convert the lead kDigits to a long integer.
    +            //
    +            // (special performance hack: start to do it using int)
    +            int iValue = (int) digits[0] - (int) '0';
    +            int iDigits = Math.min(kDigits, INT_DECIMAL_DIGITS);
    +            for (int i = 1; i < iDigits; i++) {
    +                iValue = iValue * 10 + (int) digits[i] - (int) '0';
    +            }
    +            long lValue = (long) iValue;
    +            for (int i = iDigits; i < kDigits; i++) {
    +                lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
    +            }
    +            double dValue = (double) lValue;
    +            int exp = decExponent - kDigits;
    +            //
    +            // lValue now contains a long integer with the value of
    +            // the first kDigits digits of the number.
    +            // dValue contains the (double) of the same.
    +            //
    +
    +            if (nDigits <= MAX_DECIMAL_DIGITS) {
    +                //
    +                // possibly an easy case.
    +                // We know that the digits can be represented
    +                // exactly. And if the exponent isn't too outrageous,
    +                // the whole thing can be done with one operation,
    +                // thus one rounding error.
    +                // Note that all our constructors trim all leading and
    +                // trailing zeros, so simple values (including zero)
    +                // will always end up here
    +                //
    +                if (exp == 0 || dValue == 0.0) {
    +                    return (isNegative) ? -dValue : dValue; // small floating integer
    +                }
    +                else if (exp >= 0) {
    +                    if (exp <= MAX_SMALL_TEN) {
    +                        //
    +                        // Can get the answer with one operation,
    +                        // thus one roundoff.
    +                        //
    +                        double rValue = dValue * SMALL_10_POW[exp];
    +                        return (isNegative) ? -rValue : rValue;
    +                    }
    +                    int slop = MAX_DECIMAL_DIGITS - kDigits;
    +                    if (exp <= MAX_SMALL_TEN + slop) {
    +                        //
    +                        // We can multiply dValue by 10^(slop)
    +                        // and it is still "small" and exact.
    +                        // Then we can multiply by 10^(exp-slop)
    +                        // with one rounding.
    +                        //
    +                        dValue *= SMALL_10_POW[slop];
    +                        double rValue = dValue * SMALL_10_POW[exp - slop];
    +                        return (isNegative) ? -rValue : rValue;
    +                    }
    +                    //
    +                    // Else we have a hard case with a positive exp.
    +                    //
    +                } else {
    +                    if (exp >= -MAX_SMALL_TEN) {
    +                        //
    +                        // Can get the answer in one division.
    +                        //
    +                        double rValue = dValue / SMALL_10_POW[-exp];
    +                        return (isNegative) ? -rValue : rValue;
    +                    }
    +                    //
    +                    // Else we have a hard case with a negative exp.
    +                    //
    +                }
    +            }
    +
    +            //
    +            // Harder cases:
    +            // The sum of digits plus exponent is greater than
    +            // what we think we can do with one error.
    +            //
    +            // Start by approximating the right answer by,
    +            // naively, scaling by powers of 10.
    +            //
    +            if (exp > 0) {
    +                if (decExponent > MAX_DECIMAL_EXPONENT + 1) {
    +                    //
    +                    // Lets face it. This is going to be
    +                    // Infinity. Cut to the chase.
    +                    //
    +                    return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
    +                }
    +                if ((exp & 15) != 0) {
    +                    dValue *= SMALL_10_POW[exp & 15];
    +                }
    +                if ((exp >>= 4) != 0) {
    +                    int j;
    +                    for (j = 0; exp > 1; j++, exp >>= 1) {
    +                        if ((exp & 1) != 0) {
    +                            dValue *= BIG_10_POW[j];
    +                        }
    +                    }
    +                    //
    +                    // The reason for the weird exp > 1 condition
    +                    // in the above loop was so that the last multiply
    +                    // would get unrolled. We handle it here.
    +                    // It could overflow.
    +                    //
    +                    double t = dValue * BIG_10_POW[j];
    +                    if (Double.isInfinite(t)) {
    +                        //
    +                        // It did overflow.
    +                        // Look more closely at the result.
    +                        // If the exponent is just one too large,
    +                        // then use the maximum finite as our estimate
    +                        // value. Else call the result infinity
    +                        // and punt it.
    +                        // ( I presume this could happen because
    +                        // rounding forces the result here to be
    +                        // an ULP or two larger than
    +                        // Double.MAX_VALUE ).
    +                        //
    +                        t = dValue / 2.0;
    +                        t *= BIG_10_POW[j];
    +                        if (Double.isInfinite(t)) {
    +                            return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
    +                        }
    +                        t = Double.MAX_VALUE;
    +                    }
    +                    dValue = t;
    +                }
    +            } else if (exp < 0) {
    +                exp = -exp;
    +                if (decExponent < MIN_DECIMAL_EXPONENT - 1) {
    +                    //
    +                    // Lets face it. This is going to be
    +                    // zero. Cut to the chase.
    +                    //
    +                    return (isNegative) ? -0.0 : 0.0;
    +                }
    +                if ((exp & 15) != 0) {
    +                    dValue /= SMALL_10_POW[exp & 15];
    +                }
    +                if ((exp >>= 4) != 0) {
    +                    int j;
    +                    for (j = 0; exp > 1; j++, exp >>= 1) {
    +                        if ((exp & 1) != 0) {
    +                            dValue *= TINY_10_POW[j];
    +                        }
    +                    }
    +                    //
    +                    // The reason for the weird exp > 1 condition
    +                    // in the above loop was so that the last multiply
    +                    // would get unrolled. We handle it here.
    +                    // It could underflow.
    +                    //
    +                    double t = dValue * TINY_10_POW[j];
    +                    if (t == 0.0) {
    +                        //
    +                        // It did underflow.
    +                        // Look more closely at the result.
    +                        // If the exponent is just one too small,
    +                        // then use the minimum finite as our estimate
    +                        // value. Else call the result 0.0
    +                        // and punt it.
    +                        // ( I presume this could happen because
    +                        // rounding forces the result here to be
    +                        // an ULP or two less than
    +                        // Double.MIN_VALUE ).
    +                        //
    +                        t = dValue * 2.0;
    +                        t *= TINY_10_POW[j];
    +                        if (t == 0.0) {
    +                            return (isNegative) ? -0.0 : 0.0;
    +                        }
    +                        t = Double.MIN_VALUE;
    +                    }
    +                    dValue = t;
    +                }
    +            }
    +
    +            //
    +            // dValue is now approximately the result.
    +            // The hard part is adjusting it, by comparison
    +            // with FDBigInteger arithmetic.
    +            // Formulate the EXACT big-number result as
    +            // bigD0 * 10^exp
    +            //
    +            if (nDigits > MAX_NDIGITS) {
    +                nDigits = MAX_NDIGITS + 1;
    +                digits[MAX_NDIGITS] = '1';
    +            }
    +            FDBigInteger bigD0 = new FDBigInteger(lValue, digits, kDigits, nDigits);
    +            exp = decExponent - nDigits;
    +
    +            long ieeeBits = Double.doubleToRawLongBits(dValue); // IEEE-754 bits of double candidate
    +            final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
    +            final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
    +            bigD0 = bigD0.multByPow52(D5, 0);
    +            bigD0.makeImmutable();   // prevent bigD0 modification inside correctionLoop
    +            FDBigInteger bigD = null;
    +            int prevD2 = 0;
    +
    +            correctionLoop:
    +            while (true) {
    +                // here ieeeBits can't be NaN, Infinity or zero
    +                int binexp = (int) (ieeeBits >>> EXP_SHIFT);
    +                long bigBbits = ieeeBits & DoubleConsts.SIGNIF_BIT_MASK;
    +                if (binexp > 0) {
    +                    bigBbits |= FRACT_HOB;
    +                } else { // Normalize denormalized numbers.
    +                    assert bigBbits != 0L : bigBbits; // doubleToBigInt(0.0)
    +                    int leadingZeros = Long.numberOfLeadingZeros(bigBbits);
    +                    int shift = leadingZeros - (63 - EXP_SHIFT);
    +                    bigBbits <<= shift;
    +                    binexp = 1 - shift;
    +                }
    +                binexp -= DoubleConsts.EXP_BIAS;
    +                int lowOrderZeros = Long.numberOfTrailingZeros(bigBbits);
    +                bigBbits >>>= lowOrderZeros;
    +                final int bigIntExp = binexp - EXP_SHIFT + lowOrderZeros;
    +                final int bigIntNBits = EXP_SHIFT + 1 - lowOrderZeros;
    +
    +                //
    +                // Scale bigD, bigB appropriately for
    +                // big-integer operations.
    +                // Naively, we multiply by powers of ten
    +                // and powers of two. What we actually do
    +                // is keep track of the powers of 5 and
    +                // powers of 2 we would use, then factor out
    +                // common divisors before doing the work.
    +                //
    +                int B2 = B5; // powers of 2 in bigB
    +                int D2 = D5; // powers of 2 in bigD
    +                int Ulp2;   // powers of 2 in halfUlp.
    +                if (bigIntExp >= 0) {
    +                    B2 += bigIntExp;
    +                } else {
    +                    D2 -= bigIntExp;
    +                }
    +                Ulp2 = B2;
    +                // shift bigB and bigD left by a number s. t.
    +                // halfUlp is still an integer.
    +                int hulpbias;
    +                if (binexp <= -DoubleConsts.EXP_BIAS) {
    +                    // This is going to be a denormalized number
    +                    // (if not actually zero).
    +                    // half an ULP is at 2^-(DoubleConsts.EXP_BIAS+EXP_SHIFT+1)
    +                    hulpbias = binexp + lowOrderZeros + DoubleConsts.EXP_BIAS;
    +                } else {
    +                    hulpbias = 1 + lowOrderZeros;
    +                }
    +                B2 += hulpbias;
    +                D2 += hulpbias;
    +                // if there are common factors of 2, we might just as well
    +                // factor them out, as they add nothing useful.
    +                int common2 = Math.min(B2, Math.min(D2, Ulp2));
    +                B2 -= common2;
    +                D2 -= common2;
    +                Ulp2 -= common2;
    +                // do multiplications by powers of 5 and 2
    +                FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
    +                if (bigD == null || prevD2 != D2) {
    +                    bigD = bigD0.leftShift(D2);
    +                    prevD2 = D2;
    +                }
    +                //
    +                // to recap:
    +                // bigB is the scaled-big-int version of our floating-point
    +                // candidate.
    +                // bigD is the scaled-big-int version of the exact value
    +                // as we understand it.
    +                // halfUlp is 1/2 an ulp of bigB, except for special cases
    +                // of exact powers of 2
    +                //
    +                // the plan is to compare bigB with bigD, and if the difference
    +                // is less than halfUlp, then we're satisfied. Otherwise,
    +                // use the ratio of difference to halfUlp to calculate a fudge
    +                // factor to add to the floating value, then go 'round again.
    +                //
    +                FDBigInteger diff;
    +                int cmpResult;
    +                boolean overvalue;
    +                if ((cmpResult = bigB.cmp(bigD)) > 0) {
    +                    overvalue = true; // our candidate is too big.
    +                    diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
    +                    if ((bigIntNBits == 1) && (bigIntExp > -DoubleConsts.EXP_BIAS + 1)) {
    +                        // candidate is a normalized exact power of 2 and
    +                        // is too big (larger than Double.MIN_NORMAL). We will be subtracting.
    +                        // For our purposes, ulp is the ulp of the
    +                        // next smaller range.
    +                        Ulp2 -= 1;
    +                        if (Ulp2 < 0) {
    +                            // rats. Cannot de-scale ulp this far.
    +                            // must scale diff in other direction.
    +                            Ulp2 = 0;
    +                            diff = diff.leftShift(1);
    +                        }
    +                    }
    +                } else if (cmpResult < 0) {
    +                    overvalue = false; // our candidate is too small.
    +                    diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
    +                } else {
    +                    // the candidate is exactly right!
    +                    // this happens with surprising frequency
    +                    break correctionLoop;
    +                }
    +                cmpResult = diff.cmpPow52(B5, Ulp2);
    +                if ((cmpResult) < 0) {
    +                    // difference is small.
    +                    // this is close enough
    +                    break correctionLoop;
    +                } else if (cmpResult == 0) {
    +                    // difference is exactly half an ULP
    +                    // round to some other value maybe, then finish
    +                    if ((ieeeBits & 1) != 0) { // half ties to even
    +                        ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    +                    }
    +                    break correctionLoop;
    +                } else {
    +                    // difference is non-trivial.
    +                    // could scale addend by ratio of difference to
    +                    // halfUlp here, if we bothered to compute that difference.
    +                    // Most of the time ( I hope ) it is about 1 anyway.
    +                    ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    +                    if (ieeeBits == 0 || ieeeBits == DoubleConsts.EXP_BIT_MASK) { // 0.0 or Double.POSITIVE_INFINITY
    +                        break correctionLoop; // oops. Fell off end of range.
    +                    }
    +                    continue; // try again.
    +                }
    +
    +            }
    +            if (isNegative) {
    +                ieeeBits |= DoubleConsts.SIGN_BIT_MASK;
    +            }
    +            return Double.longBitsToDouble(ieeeBits);
    +        }
    +
    +        /**
    +         * Takes a FloatingDecimal, which we presumably just scanned in,
    +         * and finds out what its value is, as a float.
    +         * This is distinct from doubleValue() to avoid the extremely
    +         * unlikely case of a double rounding error, wherein the conversion
    +         * to double has one rounding error, and the conversion of that double
    +         * to a float has another rounding error, IN THE WRONG DIRECTION,
    +         * ( because of the preference to a zero low-order bit ).
    +         */
    +        @Override
    +        public float floatValue() {
    +            int kDigits = Math.min(nDigits, SINGLE_MAX_DECIMAL_DIGITS + 1);
    +            //
    +            // convert the lead kDigits to an integer.
    +            //
    +            int iValue = (int) digits[0] - (int) '0';
    +            for (int i = 1; i < kDigits; i++) {
    +                iValue = iValue * 10 + (int) digits[i] - (int) '0';
    +            }
    +            float fValue = (float) iValue;
    +            int exp = decExponent - kDigits;
    +            //
    +            // iValue now contains an integer with the value of
    +            // the first kDigits digits of the number.
    +            // fValue contains the (float) of the same.
    +            //
    +
    +            if (nDigits <= SINGLE_MAX_DECIMAL_DIGITS) {
    +                //
    +                // possibly an easy case.
    +                // We know that the digits can be represented
    +                // exactly. And if the exponent isn't too outrageous,
    +                // the whole thing can be done with one operation,
    +                // thus one rounding error.
    +                // Note that all our constructors trim all leading and
    +                // trailing zeros, so simple values (including zero)
    +                // will always end up here.
    +                //
    +                if (exp == 0 || fValue == 0.0f) {
    +                    return (isNegative) ? -fValue : fValue; // small floating integer
    +                } else if (exp >= 0) {
    +                    if (exp <= SINGLE_MAX_SMALL_TEN) {
    +                        //
    +                        // Can get the answer with one operation,
    +                        // thus one roundoff.
    +                        //
    +                        fValue *= SINGLE_SMALL_10_POW[exp];
    +                        return (isNegative) ? -fValue : fValue;
    +                    }
    +                    int slop = SINGLE_MAX_DECIMAL_DIGITS - kDigits;
    +                    if (exp <= SINGLE_MAX_SMALL_TEN + slop) {
    +                        //
    +                        // We can multiply fValue by 10^(slop)
    +                        // and it is still "small" and exact.
    +                        // Then we can multiply by 10^(exp-slop)
    +                        // with one rounding.
    +                        //
    +                        fValue *= SINGLE_SMALL_10_POW[slop];
    +                        fValue *= SINGLE_SMALL_10_POW[exp - slop];
    +                        return (isNegative) ? -fValue : fValue;
    +                    }
    +                    //
    +                    // Else we have a hard case with a positive exp.
    +                    //
    +                } else {
    +                    if (exp >= -SINGLE_MAX_SMALL_TEN) {
    +                        //
    +                        // Can get the answer in one division.
    +                        //
    +                        fValue /= SINGLE_SMALL_10_POW[-exp];
    +                        return (isNegative) ? -fValue : fValue;
    +                    }
    +                    //
    +                    // Else we have a hard case with a negative exp.
    +                    //
    +                }
    +            } else if ((decExponent >= nDigits) && (nDigits + decExponent <= MAX_DECIMAL_DIGITS)) {
    +                //
    +                // In double-precision, this is an exact floating integer.
    +                // So we can compute to double, then shorten to float
    +                // with one round, and get the right answer.
    +                //
    +                // First, finish accumulating digits.
    +                // Then convert that integer to a double, multiply
    +                // by the appropriate power of ten, and convert to float.
    +                //
    +                long lValue = (long) iValue;
    +                for (int i = kDigits; i < nDigits; i++) {
    +                    lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
    +                }
    +                double dValue = (double) lValue;
    +                exp = decExponent - nDigits;
    +                dValue *= SMALL_10_POW[exp];
    +                fValue = (float) dValue;
    +                return (isNegative) ? -fValue : fValue;
    +
    +            }
    +            //
    +            // Harder cases:
    +            // The sum of digits plus exponent is greater than
    +            // what we think we can do with one error.
    +            //
    +            // Start by approximating the right answer by,
    +            // naively, scaling by powers of 10.
    +            // Scaling uses doubles to avoid overflow/underflow.
    +            //
    +            double dValue = fValue;
    +            if (exp > 0) {
    +                if (decExponent > SINGLE_MAX_DECIMAL_EXPONENT + 1) {
    +                    //
    +                    // Lets face it. This is going to be
    +                    // Infinity. Cut to the chase.
    +                    //
    +                    return (isNegative) ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
    +                }
    +                if ((exp & 15) != 0) {
    +                    dValue *= SMALL_10_POW[exp & 15];
    +                }
    +                if ((exp >>= 4) != 0) {
    +                    int j;
    +                    for (j = 0; exp > 0; j++, exp >>= 1) {
    +                        if ((exp & 1) != 0) {
    +                            dValue *= BIG_10_POW[j];
    +                        }
    +                    }
    +                }
    +            } else if (exp < 0) {
    +                exp = -exp;
    +                if (decExponent < SINGLE_MIN_DECIMAL_EXPONENT - 1) {
    +                    //
    +                    // Lets face it. This is going to be
    +                    // zero. Cut to the chase.
    +                    //
    +                    return (isNegative) ? -0.0f : 0.0f;
    +                }
    +                if ((exp & 15) != 0) {
    +                    dValue /= SMALL_10_POW[exp & 15];
    +                }
    +                if ((exp >>= 4) != 0) {
    +                    int j;
    +                    for (j = 0; exp > 0; j++, exp >>= 1) {
    +                        if ((exp & 1) != 0) {
    +                            dValue *= TINY_10_POW[j];
    +                        }
    +                    }
    +                }
    +            }
    +            fValue = Math.max(Float.MIN_VALUE, Math.min(Float.MAX_VALUE, (float) dValue));
    +
    +            //
    +            // fValue is now approximately the result.
    +            // The hard part is adjusting it, by comparison
    +            // with FDBigInteger arithmetic.
    +            // Formulate the EXACT big-number result as
    +            // bigD0 * 10^exp
    +            //
    +            if (nDigits > SINGLE_MAX_NDIGITS) {
    +                nDigits = SINGLE_MAX_NDIGITS + 1;
    +                digits[SINGLE_MAX_NDIGITS] = '1';
    +            }
    +            FDBigInteger bigD0 = new FDBigInteger(iValue, digits, kDigits, nDigits);
    +            exp = decExponent - nDigits;
    +
    +            int ieeeBits = Float.floatToRawIntBits(fValue); // IEEE-754 bits of float candidate
    +            final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
    +            final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
    +            bigD0 = bigD0.multByPow52(D5, 0);
    +            bigD0.makeImmutable();   // prevent bigD0 modification inside correctionLoop
    +            FDBigInteger bigD = null;
    +            int prevD2 = 0;
    +
    +            correctionLoop:
    +            while (true) {
    +                // here ieeeBits can't be NaN, Infinity or zero
    +                int binexp = ieeeBits >>> SINGLE_EXP_SHIFT;
    +                int bigBbits = ieeeBits & FloatConsts.SIGNIF_BIT_MASK;
    +                if (binexp > 0) {
    +                    bigBbits |= SINGLE_FRACT_HOB;
    +                } else { // Normalize denormalized numbers.
    +                    assert bigBbits != 0 : bigBbits; // floatToBigInt(0.0)
    +                    int leadingZeros = Integer.numberOfLeadingZeros(bigBbits);
    +                    int shift = leadingZeros - (31 - SINGLE_EXP_SHIFT);
    +                    bigBbits <<= shift;
    +                    binexp = 1 - shift;
    +                }
    +                binexp -= FloatConsts.EXP_BIAS;
    +                int lowOrderZeros = Integer.numberOfTrailingZeros(bigBbits);
    +                bigBbits >>>= lowOrderZeros;
    +                final int bigIntExp = binexp - SINGLE_EXP_SHIFT + lowOrderZeros;
    +                final int bigIntNBits = SINGLE_EXP_SHIFT + 1 - lowOrderZeros;
    +
    +                //
    +                // Scale bigD, bigB appropriately for
    +                // big-integer operations.
    +                // Naively, we multiply by powers of ten
    +                // and powers of two. What we actually do
    +                // is keep track of the powers of 5 and
    +                // powers of 2 we would use, then factor out
    +                // common divisors before doing the work.
    +                //
    +                int B2 = B5; // powers of 2 in bigB
    +                int D2 = D5; // powers of 2 in bigD
    +                int Ulp2;   // powers of 2 in halfUlp.
    +                if (bigIntExp >= 0) {
    +                    B2 += bigIntExp;
    +                } else {
    +                    D2 -= bigIntExp;
    +                }
    +                Ulp2 = B2;
    +                // shift bigB and bigD left by a number s. t.
    +                // halfUlp is still an integer.
    +                int hulpbias;
    +                if (binexp <= -FloatConsts.EXP_BIAS) {
    +                    // This is going to be a denormalized number
    +                    // (if not actually zero).
    +                    // half an ULP is at 2^-(FloatConsts.EXP_BIAS+SINGLE_EXP_SHIFT+1)
    +                    hulpbias = binexp + lowOrderZeros + FloatConsts.EXP_BIAS;
    +                } else {
    +                    hulpbias = 1 + lowOrderZeros;
    +                }
    +                B2 += hulpbias;
    +                D2 += hulpbias;
    +                // if there are common factors of 2, we might just as well
    +                // factor them out, as they add nothing useful.
    +                int common2 = Math.min(B2, Math.min(D2, Ulp2));
    +                B2 -= common2;
    +                D2 -= common2;
    +                Ulp2 -= common2;
    +                // do multiplications by powers of 5 and 2
    +                FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
    +                if (bigD == null || prevD2 != D2) {
    +                    bigD = bigD0.leftShift(D2);
    +                    prevD2 = D2;
    +                }
    +                //
    +                // to recap:
    +                // bigB is the scaled-big-int version of our floating-point
    +                // candidate.
    +                // bigD is the scaled-big-int version of the exact value
    +                // as we understand it.
    +                // halfUlp is 1/2 an ulp of bigB, except for special cases
    +                // of exact powers of 2
    +                //
    +                // the plan is to compare bigB with bigD, and if the difference
    +                // is less than halfUlp, then we're satisfied. Otherwise,
    +                // use the ratio of difference to halfUlp to calculate a fudge
    +                // factor to add to the floating value, then go 'round again.
    +                //
    +                FDBigInteger diff;
    +                int cmpResult;
    +                boolean overvalue;
    +                if ((cmpResult = bigB.cmp(bigD)) > 0) {
    +                    overvalue = true; // our candidate is too big.
    +                    diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
    +                    if ((bigIntNBits == 1) && (bigIntExp > -FloatConsts.EXP_BIAS + 1)) {
    +                        // candidate is a normalized exact power of 2 and
    +                        // is too big (larger than Float.MIN_NORMAL). We will be subtracting.
    +                        // For our purposes, ulp is the ulp of the
    +                        // next smaller range.
    +                        Ulp2 -= 1;
    +                        if (Ulp2 < 0) {
    +                            // rats. Cannot de-scale ulp this far.
    +                            // must scale diff in other direction.
    +                            Ulp2 = 0;
    +                            diff = diff.leftShift(1);
    +                        }
    +                    }
    +                } else if (cmpResult < 0) {
    +                    overvalue = false; // our candidate is too small.
    +                    diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
    +                } else {
    +                    // the candidate is exactly right!
    +                    // this happens with surprising frequency
    +                    break correctionLoop;
    +                }
    +                cmpResult = diff.cmpPow52(B5, Ulp2);
    +                if ((cmpResult) < 0) {
    +                    // difference is small.
    +                    // this is close enough
    +                    break correctionLoop;
    +                } else if (cmpResult == 0) {
    +                    // difference is exactly half an ULP
    +                    // round to some other value maybe, then finish
    +                    if ((ieeeBits & 1) != 0) { // half ties to even
    +                        ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    +                    }
    +                    break correctionLoop;
    +                } else {
    +                    // difference is non-trivial.
    +                    // could scale addend by ratio of difference to
    +                    // halfUlp here, if we bothered to compute that difference.
    +                    // Most of the time ( I hope ) it is about 1 anyway.
    +                    ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    +                    if (ieeeBits == 0 || ieeeBits == FloatConsts.EXP_BIT_MASK) { // 0.0 or Float.POSITIVE_INFINITY
    +                        break correctionLoop; // oops. Fell off end of range.
    +                    }
    +                    continue; // try again.
    +                }
    +
    +            }
    +            if (isNegative) {
    +                ieeeBits |= FloatConsts.SIGN_BIT_MASK;
    +            }
    +            return Float.intBitsToFloat(ieeeBits);
    +        }
    +
    +
    +        /**
    +         * All the positive powers of 10 that can be
    +         * represented exactly in double/float.
    +         */
    +        private static final double[] SMALL_10_POW = {
    +            1.0e0,
    +            1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5,
    +            1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10,
    +            1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15,
    +            1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20,
    +            1.0e21, 1.0e22
    +        };
    +
    +        private static final float[] SINGLE_SMALL_10_POW = {
    +            1.0e0f,
    +            1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
    +            1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
    +        };
    +
    +        private static final double[] BIG_10_POW = {
    +            1e16, 1e32, 1e64, 1e128, 1e256 };
    +        private static final double[] TINY_10_POW = {
    +            1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
    +
    +        private static final int MAX_SMALL_TEN = SMALL_10_POW.length-1;
    +        private static final int SINGLE_MAX_SMALL_TEN = SINGLE_SMALL_10_POW.length-1;
    +
    +    }
    +
    +    /**
    +     * Returns a BinaryToASCIIConverter for a double.
    +     * The returned object is a ThreadLocal variable of this class.
    +     *
    +     * @param d The double precision value to convert.
    +     * @return The converter.
    +     */
    +    public static BinaryToASCIIConverter getBinaryToASCIIConverter(double d) {
    +        return getBinaryToASCIIConverter(d, true);
    +    }
    +
    +    /**
    +     * Returns a BinaryToASCIIConverter for a double.
    +     * The returned object is a ThreadLocal variable of this class.
    +     *
    +     * @param d The double precision value to convert.
    +     * @param isCompatibleFormat
    +     * @return The converter.
    +     */
    +    static BinaryToASCIIConverter getBinaryToASCIIConverter(double d, boolean isCompatibleFormat) {
    +        long dBits = Double.doubleToRawLongBits(d);
    +        boolean isNegative = (dBits&DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign
    +        long fractBits = dBits & DoubleConsts.SIGNIF_BIT_MASK;
    +        int  binExp = (int)( (dBits&DoubleConsts.EXP_BIT_MASK) >> EXP_SHIFT );
    +        // Discover obvious special cases of NaN and Infinity.
    +        if ( binExp == (int)(DoubleConsts.EXP_BIT_MASK>>EXP_SHIFT) ) {
    +            if ( fractBits == 0L ){
    +                return isNegative ? B2AC_NEGATIVE_INFINITY : B2AC_POSITIVE_INFINITY;
    +            } else {
    +                return B2AC_NOT_A_NUMBER;
    +            }
    +        }
    +        // Finish unpacking
    +        // Normalize denormalized numbers.
    +        // Insert assumed high-order bit for normalized numbers.
    +        // Subtract exponent bias.
    +        int  nSignificantBits;
    +        if ( binExp == 0 ){
    +            if ( fractBits == 0L ){
    +                // not a denorm, just a 0!
    +                return isNegative ? B2AC_NEGATIVE_ZERO : B2AC_POSITIVE_ZERO;
    +            }
    +            int leadingZeros = Long.numberOfLeadingZeros(fractBits);
    +            int shift = leadingZeros-(63-EXP_SHIFT);
    +            fractBits <<= shift;
    +            binExp = 1 - shift;
    +            nSignificantBits =  64-leadingZeros; // recall binExp is  - shift count.
    +        } else {
    +            fractBits |= FRACT_HOB;
    +            nSignificantBits = EXP_SHIFT+1;
    +        }
    +        binExp -= DoubleConsts.EXP_BIAS;
    +        BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
    +        buf.setSign(isNegative);
    +        // call the routine that actually does all the hard work.
    +        buf.dtoa(binExp, fractBits, nSignificantBits, isCompatibleFormat);
    +        return buf;
    +    }
    +
    +    private static BinaryToASCIIConverter getBinaryToASCIIConverter(float f) {
    +        int fBits = Float.floatToRawIntBits( f );
    +        boolean isNegative = (fBits&FloatConsts.SIGN_BIT_MASK) != 0;
    +        int fractBits = fBits&FloatConsts.SIGNIF_BIT_MASK;
    +        int binExp = (fBits&FloatConsts.EXP_BIT_MASK) >> SINGLE_EXP_SHIFT;
    +        // Discover obvious special cases of NaN and Infinity.
    +        if ( binExp == (FloatConsts.EXP_BIT_MASK>>SINGLE_EXP_SHIFT) ) {
    +            if ( fractBits == 0L ){
    +                return isNegative ? B2AC_NEGATIVE_INFINITY : B2AC_POSITIVE_INFINITY;
    +            } else {
    +                return B2AC_NOT_A_NUMBER;
    +            }
    +        }
    +        // Finish unpacking
    +        // Normalize denormalized numbers.
    +        // Insert assumed high-order bit for normalized numbers.
    +        // Subtract exponent bias.
    +        int  nSignificantBits;
    +        if ( binExp == 0 ){
    +            if ( fractBits == 0 ){
    +                // not a denorm, just a 0!
    +                return isNegative ? B2AC_NEGATIVE_ZERO : B2AC_POSITIVE_ZERO;
    +            }
    +            int leadingZeros = Integer.numberOfLeadingZeros(fractBits);
    +            int shift = leadingZeros-(31-SINGLE_EXP_SHIFT);
    +            fractBits <<= shift;
    +            binExp = 1 - shift;
    +            nSignificantBits =  32 - leadingZeros; // recall binExp is  - shift count.
    +        } else {
    +            fractBits |= SINGLE_FRACT_HOB;
    +            nSignificantBits = SINGLE_EXP_SHIFT+1;
    +        }
    +        binExp -= FloatConsts.EXP_BIAS;
    +        BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
    +        buf.setSign(isNegative);
    +        // call the routine that actually does all the hard work.
    +        buf.dtoa(binExp, ((long)fractBits)<<(EXP_SHIFT-SINGLE_EXP_SHIFT), nSignificantBits, true);
    +        return buf;
    +    }
    +
    +    @SuppressWarnings("fallthrough")
    +    static ASCIIToBinaryConverter readJavaFormatString( String in ) throws NumberFormatException {
    +        boolean isNegative = false;
    +        boolean signSeen   = false;
    +        int     decExp;
    +        char    c;
    +
    +    parseNumber:
    +        try{
    +            in = in.trim(); // don't fool around with white space.
    +                            // throws NullPointerException if null
    +            int len = in.length();
    +            if ( len == 0 ) {
    +                throw new NumberFormatException("empty String");
    +            }
    +            int i = 0;
    +            switch (in.charAt(i)){
    +            case '-':
    +                isNegative = true;
    +                //FALLTHROUGH
    +            case '+':
    +                i++;
    +                signSeen = true;
    +            }
    +            c = in.charAt(i);
    +            if(c == 'N') { // Check for NaN
    +                if((len-i)==NAN_LENGTH && in.indexOf(NAN_REP,i)==i) {
    +                    return A2BC_NOT_A_NUMBER;
    +                }
    +                // something went wrong, throw exception
    +                break parseNumber;
    +            } else if(c == 'I') { // Check for Infinity strings
    +                if((len-i)==INFINITY_LENGTH && in.indexOf(INFINITY_REP,i)==i) {
    +                    return isNegative? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
    +                }
    +                // something went wrong, throw exception
    +                break parseNumber;
    +            } else if (c == '0')  { // check for hexadecimal floating-point number
    +                if (len > i+1 ) {
    +                    char ch = in.charAt(i+1);
    +                    if (ch == 'x' || ch == 'X' ) { // possible hex string
    +                        return parseHexString(in);
    +                    }
    +                }
    +            }  // look for and process decimal floating-point string
    +
    +            char[] digits = new char[ len ];
    +            int    nDigits= 0;
    +            boolean decSeen = false;
    +            int decPt = 0;
    +            int nLeadZero = 0;
    +            int nTrailZero= 0;
    +
    +        skipLeadingZerosLoop:
    +            while (i < len) {
    +                c = in.charAt(i);
    +                if (c == '0') {
    +                    nLeadZero++;
    +                } else if (c == '.') {
    +                    if (decSeen) {
    +                        // already saw one ., this is the 2nd.
    +                        throw new NumberFormatException("multiple points");
    +                    }
    +                    decPt = i;
    +                    if (signSeen) {
    +                        decPt -= 1;
    +                    }
    +                    decSeen = true;
    +                } else {
    +                    break skipLeadingZerosLoop;
    +                }
    +                i++;
    +            }
    +        digitLoop:
    +            while (i < len) {
    +                c = in.charAt(i);
    +                if (c >= '1' && c <= '9') {
    +                    digits[nDigits++] = c;
    +                    nTrailZero = 0;
    +                } else if (c == '0') {
    +                    digits[nDigits++] = c;
    +                    nTrailZero++;
    +                } else if (c == '.') {
    +                    if (decSeen) {
    +                        // already saw one ., this is the 2nd.
    +                        throw new NumberFormatException("multiple points");
    +                    }
    +                    decPt = i;
    +                    if (signSeen) {
    +                        decPt -= 1;
    +                    }
    +                    decSeen = true;
    +                } else {
    +                    break digitLoop;
    +                }
    +                i++;
    +            }
    +            nDigits -=nTrailZero;
    +            //
    +            // At this point, we've scanned all the digits and decimal
    +            // point we're going to see. Trim off leading and trailing
    +            // zeros, which will just confuse us later, and adjust
    +            // our initial decimal exponent accordingly.
    +            // To review:
    +            // we have seen i total characters.
    +            // nLeadZero of them were zeros before any other digits.
    +            // nTrailZero of them were zeros after any other digits.
    +            // if ( decSeen ), then a . was seen after decPt characters
    +            // ( including leading zeros which have been discarded )
    +            // nDigits characters were neither lead nor trailing
    +            // zeros, nor point
    +            //
    +            //
    +            // special hack: if we saw no non-zero digits, then the
    +            // answer is zero!
    +            // Unfortunately, we feel honor-bound to keep parsing!
    +            //
    +            boolean isZero = (nDigits == 0);
    +            if ( isZero &&  nLeadZero == 0 ){
    +                // we saw NO DIGITS AT ALL,
    +                // not even a crummy 0!
    +                // this is not allowed.
    +                break parseNumber; // go throw exception
    +            }
    +            //
    +            // Our initial exponent is decPt, adjusted by the number of
    +            // discarded zeros. Or, if there was no decPt,
    +            // then its just nDigits adjusted by discarded trailing zeros.
    +            //
    +            if ( decSeen ){
    +                decExp = decPt - nLeadZero;
    +            } else {
    +                decExp = nDigits + nTrailZero;
    +            }
    +
    +            //
    +            // Look for 'e' or 'E' and an optionally signed integer.
    +            //
    +            if ( (i < len) &&  (((c = in.charAt(i) )=='e') || (c == 'E') ) ){
    +                int expSign = 1;
    +                int expVal  = 0;
    +                int reallyBig = Integer.MAX_VALUE / 10;
    +                boolean expOverflow = false;
    +                switch( in.charAt(++i) ){
    +                case '-':
    +                    expSign = -1;
    +                    //FALLTHROUGH
    +                case '+':
    +                    i++;
    +                }
    +                int expAt = i;
    +            expLoop:
    +                while ( i < len  ){
    +                    if ( expVal >= reallyBig ){
    +                        // the next character will cause integer
    +                        // overflow.
    +                        expOverflow = true;
    +                    }
    +                    c = in.charAt(i++);
    +                    if(c>='0' && c<='9') {
    +                        expVal = expVal*10 + ( (int)c - (int)'0' );
    +                    } else {
    +                        i--;           // back up.
    +                        break expLoop; // stop parsing exponent.
    +                    }
    +                }
    +                int expLimit = BIG_DECIMAL_EXPONENT + nDigits + nTrailZero;
    +                if (expOverflow || (expVal > expLimit)) {
    +                    // There is still a chance that the exponent will be safe to
    +                    // use: if it would eventually decrease due to a negative
    +                    // decExp, and that number is below the limit.  We check for
    +                    // that here.
    +                    if (!expOverflow && (expSign == 1 && decExp < 0)
    +                            && (expVal + decExp) < expLimit) {
    +                        // Cannot overflow: adding a positive and negative number.
    +                        decExp += expVal;
    +                    } else {
    +                        //
    +                        // The intent here is to end up with
    +                        // infinity or zero, as appropriate.
    +                        // The reason for yielding such a small decExponent,
    +                        // rather than something intuitive such as
    +                        // expSign*Integer.MAX_VALUE, is that this value
    +                        // is subject to further manipulation in
    +                        // doubleValue() and floatValue(), and I don't want
    +                        // it to be able to cause overflow there!
    +                        // (The only way we can get into trouble here is for
    +                        // really outrageous nDigits+nTrailZero, such as 2
    +                        // billion.)
    +                        //
    +                        decExp = expSign * expLimit;
    +                    }
    +                } else {
    +                    // this should not overflow, since we tested
    +                    // for expVal > (MAX+N), where N >= abs(decExp)
    +                    decExp = decExp + expSign*expVal;
    +                }
    +
    +                // if we saw something not a digit ( or end of string )
    +                // after the [Ee][+-], without seeing any digits at all
    +                // this is certainly an error. If we saw some digits,
    +                // but then some trailing garbage, that might be ok.
    +                // so we just fall through in that case.
    +                // HUMBUG
    +                if ( i == expAt ) {
    +                    break parseNumber; // certainly bad
    +                }
    +            }
    +            //
    +            // We parsed everything we could.
    +            // If there are leftovers, then this is not good input!
    +            //
    +            if ( i < len &&
    +                ((i != len - 1) ||
    +                (in.charAt(i) != 'f' &&
    +                 in.charAt(i) != 'F' &&
    +                 in.charAt(i) != 'd' &&
    +                 in.charAt(i) != 'D'))) {
    +                break parseNumber; // go throw exception
    +            }
    +            if(isZero) {
    +                return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
    +            }
    +            return new ASCIIToBinaryBuffer(isNegative, decExp, digits, nDigits);
    +        } catch ( StringIndexOutOfBoundsException e ){ }
    +        throw new NumberFormatException("For input string: \"" + in + "\"");
    +    }
    +
    +    private static class HexFloatPattern {
    +        /**
    +         * Grammar is compatible with hexadecimal floating-point constants
    +         * described in section 6.4.4.2 of the C99 specification.
    +         */
    +        private static final Pattern VALUE = Pattern.compile(
    +                   //1           234                   56                7                   8      9
    +                    "([-+])?0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?"
    +                    );
    +    }
    +
    +    /**
    +     * Converts string s to a suitable floating decimal; uses the
    +     * double constructor and sets the roundDir variable appropriately
    +     * in case the value is later converted to a float.
    +     *
    +     * @param s The String to parse.
    +     */
    +   static ASCIIToBinaryConverter parseHexString(String s) {
    +            // Verify string is a member of the hexadecimal floating-point
    +            // string language.
    +            Matcher m = HexFloatPattern.VALUE.matcher(s);
    +            boolean validInput = m.matches();
    +            if (!validInput) {
    +                // Input does not match pattern
    +                throw new NumberFormatException("For input string: \"" + s + "\"");
    +            } else { // validInput
    +                //
    +                // We must isolate the sign, significand, and exponent
    +                // fields.  The sign value is straightforward.  Since
    +                // floating-point numbers are stored with a normalized
    +                // representation, the significand and exponent are
    +                // interrelated.
    +                //
    +                // After extracting the sign, we normalized the
    +                // significand as a hexadecimal value, calculating an
    +                // exponent adjust for any shifts made during
    +                // normalization.  If the significand is zero, the
    +                // exponent doesn't need to be examined since the output
    +                // will be zero.
    +                //
    +                // Next the exponent in the input string is extracted.
    +                // Afterwards, the significand is normalized as a *binary*
    +                // value and the input value's normalized exponent can be
    +                // computed.  The significand bits are copied into a
    +                // double significand; if the string has more logical bits
    +                // than can fit in a double, the extra bits affect the
    +                // round and sticky bits which are used to round the final
    +                // value.
    +                //
    +                //  Extract significand sign
    +                String group1 = m.group(1);
    +                boolean isNegative = ((group1 != null) && group1.equals("-"));
    +
    +                //  Extract Significand magnitude
    +                //
    +                // Based on the form of the significand, calculate how the
    +                // binary exponent needs to be adjusted to create a
    +                // normalized//hexadecimal* floating-point number; that
    +                // is, a number where there is one nonzero hex digit to
    +                // the left of the (hexa)decimal point.  Since we are
    +                // adjusting a binary, not hexadecimal exponent, the
    +                // exponent is adjusted by a multiple of 4.
    +                //
    +                // There are a number of significand scenarios to consider;
    +                // letters are used in indicate nonzero digits:
    +                //
    +                // 1. 000xxxx       =>      x.xxx   normalized
    +                //    increase exponent by (number of x's - 1)*4
    +                //
    +                // 2. 000xxx.yyyy =>        x.xxyyyy        normalized
    +                //    increase exponent by (number of x's - 1)*4
    +                //
    +                // 3. .000yyy  =>   y.yy    normalized
    +                //    decrease exponent by (number of zeros + 1)*4
    +                //
    +                // 4. 000.00000yyy => y.yy normalized
    +                //    decrease exponent by (number of zeros to right of point + 1)*4
    +                //
    +                // If the significand is exactly zero, return a properly
    +                // signed zero.
    +                //
    +
    +                String significandString = null;
    +                int signifLength = 0;
    +                int exponentAdjust = 0;
    +                {
    +                    int leftDigits = 0; // number of meaningful digits to
    +                    // left of "decimal" point
    +                    // (leading zeros stripped)
    +                    int rightDigits = 0; // number of digits to right of
    +                    // "decimal" point; leading zeros
    +                    // must always be accounted for
    +                    //
    +                    // The significand is made up of either
    +                    //
    +                    // 1. group 4 entirely (integer portion only)
    +                    //
    +                    // OR
    +                    //
    +                    // 2. the fractional portion from group 7 plus any
    +                    // (optional) integer portions from group 6.
    +                    //
    +                    String group4;
    +                    if ((group4 = m.group(4)) != null) {  // Integer-only significand
    +                        // Leading zeros never matter on the integer portion
    +                        significandString = stripLeadingZeros(group4);
    +                        leftDigits = significandString.length();
    +                    } else {
    +                        // Group 6 is the optional integer; leading zeros
    +                        // never matter on the integer portion
    +                        String group6 = stripLeadingZeros(m.group(6));
    +                        leftDigits = group6.length();
    +
    +                        // fraction
    +                        String group7 = m.group(7);
    +                        rightDigits = group7.length();
    +
    +                        // Turn "integer.fraction" into "integer"+"fraction"
    +                        significandString =
    +                                ((group6 == null) ? "" : group6) + // is the null
    +                                        // check necessary?
    +                                        group7;
    +                    }
    +
    +                    significandString = stripLeadingZeros(significandString);
    +                    signifLength = significandString.length();
    +
    +                    //
    +                    // Adjust exponent as described above
    +                    //
    +                    if (leftDigits >= 1) {  // Cases 1 and 2
    +                        exponentAdjust = 4 * (leftDigits - 1);
    +                    } else {                // Cases 3 and 4
    +                        exponentAdjust = -4 * (rightDigits - signifLength + 1);
    +                    }
    +
    +                    // If the significand is zero, the exponent doesn't
    +                    // matter; return a properly signed zero.
    +
    +                    if (signifLength == 0) { // Only zeros in input
    +                        return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
    +                    }
    +                }
    +
    +                //  Extract Exponent
    +                //
    +                // Use an int to read in the exponent value; this should
    +                // provide more than sufficient range for non-contrived
    +                // inputs.  If reading the exponent in as an int does
    +                // overflow, examine the sign of the exponent and
    +                // significand to determine what to do.
    +                //
    +                String group8 = m.group(8);
    +                boolean positiveExponent = (group8 == null) || group8.equals("+");
    +                long unsignedRawExponent;
    +                try {
    +                    unsignedRawExponent = Integer.parseInt(m.group(9));
    +                }
    +                catch (NumberFormatException e) {
    +                    // At this point, we know the exponent is
    +                    // syntactically well-formed as a sequence of
    +                    // digits.  Therefore, if an NumberFormatException
    +                    // is thrown, it must be due to overflowing int's
    +                    // range.  Also, at this point, we have already
    +                    // checked for a zero significand.  Thus the signs
    +                    // of the exponent and significand determine the
    +                    // final result:
    +                    //
    +                    //                      significand
    +                    //                      +               -
    +                    // exponent     +       +infinity       -infinity
    +                    //              -       +0.0            -0.0
    +                    return isNegative ?
    +                              (positiveExponent ? A2BC_NEGATIVE_INFINITY : A2BC_NEGATIVE_ZERO)
    +                            : (positiveExponent ? A2BC_POSITIVE_INFINITY : A2BC_POSITIVE_ZERO);
    +
    +                }
    +
    +                long rawExponent =
    +                        (positiveExponent ? 1L : -1L) * // exponent sign
    +                                unsignedRawExponent;            // exponent magnitude
    +
    +                // Calculate partially adjusted exponent
    +                long exponent = rawExponent + exponentAdjust;
    +
    +                // Starting copying non-zero bits into proper position in
    +                // a long; copy explicit bit too; this will be masked
    +                // later for normal values.
    +
    +                boolean round = false;
    +                boolean sticky = false;
    +                int nextShift = 0;
    +                long significand = 0L;
    +                // First iteration is different, since we only copy
    +                // from the leading significand bit; one more exponent
    +                // adjust will be needed...
    +
    +                // IMPORTANT: make leadingDigit a long to avoid
    +                // surprising shift semantics!
    +                long leadingDigit = getHexDigit(significandString, 0);
    +
    +                //
    +                // Left shift the leading digit (53 - (bit position of
    +                // leading 1 in digit)); this sets the top bit of the
    +                // significand to 1.  The nextShift value is adjusted
    +                // to take into account the number of bit positions of
    +                // the leadingDigit actually used.  Finally, the
    +                // exponent is adjusted to normalize the significand
    +                // as a binary value, not just a hex value.
    +                //
    +                if (leadingDigit == 1) {
    +                    significand |= leadingDigit << 52;
    +                    nextShift = 52 - 4;
    +                    // exponent += 0
    +                } else if (leadingDigit <= 3) { // [2, 3]
    +                    significand |= leadingDigit << 51;
    +                    nextShift = 52 - 5;
    +                    exponent += 1;
    +                } else if (leadingDigit <= 7) { // [4, 7]
    +                    significand |= leadingDigit << 50;
    +                    nextShift = 52 - 6;
    +                    exponent += 2;
    +                } else if (leadingDigit <= 15) { // [8, f]
    +                    significand |= leadingDigit << 49;
    +                    nextShift = 52 - 7;
    +                    exponent += 3;
    +                } else {
    +                    throw new AssertionError("Result from digit conversion too large!");
    +                }
    +                // The preceding if-else could be replaced by a single
    +                // code block based on the high-order bit set in
    +                // leadingDigit.  Given leadingOnePosition,
    +
    +                // significand |= leadingDigit << (SIGNIFICAND_WIDTH - leadingOnePosition);
    +                // nextShift = 52 - (3 + leadingOnePosition);
    +                // exponent += (leadingOnePosition-1);
    +
    +                //
    +                // Now the exponent variable is equal to the normalized
    +                // binary exponent.  Code below will make representation
    +                // adjustments if the exponent is incremented after
    +                // rounding (includes overflows to infinity) or if the
    +                // result is subnormal.
    +                //
    +
    +                // Copy digit into significand until the significand can't
    +                // hold another full hex digit or there are no more input
    +                // hex digits.
    +                int i = 0;
    +                for (i = 1;
    +                     i < signifLength && nextShift >= 0;
    +                     i++) {
    +                    long currentDigit = getHexDigit(significandString, i);
    +                    significand |= (currentDigit << nextShift);
    +                    nextShift -= 4;
    +                }
    +
    +                // After the above loop, the bulk of the string is copied.
    +                // Now, we must copy any partial hex digits into the
    +                // significand AND compute the round bit and start computing
    +                // sticky bit.
    +
    +                if (i < signifLength) { // at least one hex input digit exists
    +                    long currentDigit = getHexDigit(significandString, i);
    +
    +                    // from nextShift, figure out how many bits need
    +                    // to be copied, if any
    +                    switch (nextShift) { // must be negative
    +                        case -1:
    +                            // three bits need to be copied in; can
    +                            // set round bit
    +                            significand |= ((currentDigit & 0xEL) >> 1);
    +                            round = (currentDigit & 0x1L) != 0L;
    +                            break;
    +
    +                        case -2:
    +                            // two bits need to be copied in; can
    +                            // set round and start sticky
    +                            significand |= ((currentDigit & 0xCL) >> 2);
    +                            round = (currentDigit & 0x2L) != 0L;
    +                            sticky = (currentDigit & 0x1L) != 0;
    +                            break;
    +
    +                        case -3:
    +                            // one bit needs to be copied in
    +                            significand |= ((currentDigit & 0x8L) >> 3);
    +                            // Now set round and start sticky, if possible
    +                            round = (currentDigit & 0x4L) != 0L;
    +                            sticky = (currentDigit & 0x3L) != 0;
    +                            break;
    +
    +                        case -4:
    +                            // all bits copied into significand; set
    +                            // round and start sticky
    +                            round = ((currentDigit & 0x8L) != 0);  // is top bit set?
    +                            // nonzeros in three low order bits?
    +                            sticky = (currentDigit & 0x7L) != 0;
    +                            break;
    +
    +                        default:
    +                            throw new AssertionError("Unexpected shift distance remainder.");
    +                            // break;
    +                    }
    +
    +                    // Round is set; sticky might be set.
    +
    +                    // For the sticky bit, it suffices to check the
    +                    // current digit and test for any nonzero digits in
    +                    // the remaining unprocessed input.
    +                    i++;
    +                    while (i < signifLength && !sticky) {
    +                        currentDigit = getHexDigit(significandString, i);
    +                        sticky = sticky || (currentDigit != 0);
    +                        i++;
    +                    }
    +
    +                }
    +                // else all of string was seen, round and sticky are
    +                // correct as false.
    +
    +                // Float calculations
    +                int floatBits = isNegative ? FloatConsts.SIGN_BIT_MASK : 0;
    +                if (exponent >= FloatConsts.MIN_EXPONENT) {
    +                    if (exponent > FloatConsts.MAX_EXPONENT) {
    +                        // Float.POSITIVE_INFINITY
    +                        floatBits |= FloatConsts.EXP_BIT_MASK;
    +                    } else {
    +                        int threshShift = DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH - 1;
    +                        boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky;
    +                        int iValue = (int) (significand >>> threshShift);
    +                        if ((iValue & 3) != 1 || floatSticky) {
    +                            iValue++;
    +                        }
    +                        floatBits |= (((((int) exponent) + (FloatConsts.EXP_BIAS - 1))) << SINGLE_EXP_SHIFT) + (iValue >> 1);
    +                    }
    +                } else {
    +                    if (exponent < FloatConsts.MIN_SUB_EXPONENT - 1) {
    +                        // 0
    +                    } else {
    +                        // exponent == -127 ==> threshShift = 53 - 2 + (-149) - (-127) = 53 - 24
    +                        int threshShift = (int) ((DoubleConsts.SIGNIFICAND_WIDTH - 2 + FloatConsts.MIN_SUB_EXPONENT) - exponent);
    +                        assert threshShift >= DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH;
    +                        assert threshShift < DoubleConsts.SIGNIFICAND_WIDTH;
    +                        boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky;
    +                        int iValue = (int) (significand >>> threshShift);
    +                        if ((iValue & 3) != 1 || floatSticky) {
    +                            iValue++;
    +                        }
    +                        floatBits |= iValue >> 1;
    +                    }
    +                }
    +                float fValue = Float.intBitsToFloat(floatBits);
    +
    +                // Check for overflow and update exponent accordingly.
    +                if (exponent > DoubleConsts.MAX_EXPONENT) {         // Infinite result
    +                    // overflow to properly signed infinity
    +                    return isNegative ? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
    +                } else {  // Finite return value
    +                    if (exponent <= DoubleConsts.MAX_EXPONENT && // (Usually) normal result
    +                            exponent >= DoubleConsts.MIN_EXPONENT) {
    +
    +                        // The result returned in this block cannot be a
    +                        // zero or subnormal; however after the
    +                        // significand is adjusted from rounding, we could
    +                        // still overflow in infinity.
    +
    +                        // AND exponent bits into significand; if the
    +                        // significand is incremented and overflows from
    +                        // rounding, this combination will update the
    +                        // exponent correctly, even in the case of
    +                        // Double.MAX_VALUE overflowing to infinity.
    +
    +                        significand = ((( exponent +
    +                                (long) DoubleConsts.EXP_BIAS) <<
    +                                (DoubleConsts.SIGNIFICAND_WIDTH - 1))
    +                                & DoubleConsts.EXP_BIT_MASK) |
    +                                (DoubleConsts.SIGNIF_BIT_MASK & significand);
    +
    +                    } else {  // Subnormal or zero
    +                        // (exponent < DoubleConsts.MIN_EXPONENT)
    +
    +                        if (exponent < (DoubleConsts.MIN_SUB_EXPONENT - 1)) {
    +                            // No way to round back to nonzero value
    +                            // regardless of significand if the exponent is
    +                            // less than -1075.
    +                            return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
    +                        } else { //  -1075 <= exponent <= MIN_EXPONENT -1 = -1023
    +                            //
    +                            // Find bit position to round to; recompute
    +                            // round and sticky bits, and shift
    +                            // significand right appropriately.
    +                            //
    +
    +                            sticky = sticky || round;
    +                            round = false;
    +
    +                            // Number of bits of significand to preserve is
    +                            // exponent - abs_min_exp +1
    +                            // check:
    +                            // -1075 +1074 + 1 = 0
    +                            // -1023 +1074 + 1 = 52
    +
    +                            int bitsDiscarded = 53 -
    +                                    ((int) exponent - DoubleConsts.MIN_SUB_EXPONENT + 1);
    +                            assert bitsDiscarded >= 1 && bitsDiscarded <= 53;
    +
    +                            // What to do here:
    +                            // First, isolate the new round bit
    +                            round = (significand & (1L << (bitsDiscarded - 1))) != 0L;
    +                            if (bitsDiscarded > 1) {
    +                                // create mask to update sticky bits; low
    +                                // order bitsDiscarded bits should be 1
    +                                long mask = ~((~0L) << (bitsDiscarded - 1));
    +                                sticky = sticky || ((significand & mask) != 0L);
    +                            }
    +
    +                            // Now, discard the bits
    +                            significand = significand >> bitsDiscarded;
    +
    +                            significand = ((((long) (DoubleConsts.MIN_EXPONENT - 1) + // subnorm exp.
    +                                    (long) DoubleConsts.EXP_BIAS) <<
    +                                    (DoubleConsts.SIGNIFICAND_WIDTH - 1))
    +                                    & DoubleConsts.EXP_BIT_MASK) |
    +                                    (DoubleConsts.SIGNIF_BIT_MASK & significand);
    +                        }
    +                    }
    +
    +                    // The significand variable now contains the currently
    +                    // appropriate exponent bits too.
    +
    +                    //
    +                    // Determine if significand should be incremented;
    +                    // making this determination depends on the least
    +                    // significant bit and the round and sticky bits.
    +                    //
    +                    // Round to nearest even rounding table, adapted from
    +                    // table 4.7 in "Computer Arithmetic" by IsraelKoren.
    +                    // The digit to the left of the "decimal" point is the
    +                    // least significant bit, the digits to the right of
    +                    // the point are the round and sticky bits
    +                    //
    +                    // Number       Round(x)
    +                    // x0.00        x0.
    +                    // x0.01        x0.
    +                    // x0.10        x0.
    +                    // x0.11        x1. = x0. +1
    +                    // x1.00        x1.
    +                    // x1.01        x1.
    +                    // x1.10        x1. + 1
    +                    // x1.11        x1. + 1
    +                    //
    +                    boolean leastZero = ((significand & 1L) == 0L);
    +                    if ((leastZero && round && sticky) ||
    +                            ((!leastZero) && round)) {
    +                        significand++;
    +                    }
    +
    +                    double value = isNegative ?
    +                            Double.longBitsToDouble(significand | DoubleConsts.SIGN_BIT_MASK) :
    +                            Double.longBitsToDouble(significand );
    +
    +                    return new PreparedASCIIToBinaryBuffer(value, fValue);
    +                }
    +            }
    +    }
    +
    +    /**
    +     * Returns s with any leading zeros removed.
    +     */
    +    static String stripLeadingZeros(String s) {
    +//        return  s.replaceFirst("^0+", "");
    +        if(!s.isEmpty() && s.charAt(0)=='0') {
    +            for(int i=1; iposition
    +     * of string s.
    +     */
    +    static int getHexDigit(String s, int position) {
    +        int value = Character.digit(s.charAt(position), 16);
    +        if (value <= -1 || value >= 16) {
    +            throw new AssertionError("Unexpected failure of digit conversion of " +
    +                                     s.charAt(position));
    +        }
    +        return value;
    +    }
    +}
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/jdk/internal/math/FormattedFloatingDecimal.java
    --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    +++ b/jdk/src/java.base/share/classes/jdk/internal/math/FormattedFloatingDecimal.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -0,0 +1,349 @@
    +/*
    + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
    + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    + *
    + * This code is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License version 2 only, as
    + * published by the Free Software Foundation.  Oracle designates this
    + * particular file as subject to the "Classpath" exception as provided
    + * by Oracle in the LICENSE file that accompanied this code.
    + *
    + * This code is distributed in the hope that it will be useful, but WITHOUT
    + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    + * version 2 for more details (a copy is included in the LICENSE file that
    + * accompanied this code).
    + *
    + * You should have received a copy of the GNU General Public License version
    + * 2 along with this work; if not, write to the Free Software Foundation,
    + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    + *
    + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    + * or visit www.oracle.com if you need additional information or have any
    + * questions.
    + */
    +
    +package jdk.internal.math;
    +
    +import java.util.Arrays;
    +
    +public class FormattedFloatingDecimal{
    +
    +    public enum Form { SCIENTIFIC, COMPATIBLE, DECIMAL_FLOAT, GENERAL };
    +
    +
    +    public static FormattedFloatingDecimal valueOf(double d, int precision, Form form){
    +        FloatingDecimal.BinaryToASCIIConverter fdConverter =
    +                FloatingDecimal.getBinaryToASCIIConverter(d, form == Form.COMPATIBLE);
    +        return new FormattedFloatingDecimal(precision,form, fdConverter);
    +    }
    +
    +    private int decExponentRounded;
    +    private char[] mantissa;
    +    private char[] exponent;
    +
    +    private static final ThreadLocal threadLocalCharBuffer =
    +            new ThreadLocal() {
    +                @Override
    +                protected Object initialValue() {
    +                    return new char[20];
    +                }
    +            };
    +
    +    private static char[] getBuffer(){
    +        return (char[]) threadLocalCharBuffer.get();
    +    }
    +
    +    private FormattedFloatingDecimal(int precision, Form form, FloatingDecimal.BinaryToASCIIConverter fdConverter) {
    +        if (fdConverter.isExceptional()) {
    +            this.mantissa = fdConverter.toJavaFormatString().toCharArray();
    +            this.exponent = null;
    +            return;
    +        }
    +        char[] digits = getBuffer();
    +        int nDigits = fdConverter.getDigits(digits);
    +        int decExp = fdConverter.getDecimalExponent();
    +        int exp;
    +        boolean isNegative = fdConverter.isNegative();
    +        switch (form) {
    +            case COMPATIBLE:
    +                exp = decExp;
    +                this.decExponentRounded = exp;
    +                fillCompatible(precision, digits, nDigits, exp, isNegative);
    +                break;
    +            case DECIMAL_FLOAT:
    +                exp = applyPrecision(decExp, digits, nDigits, decExp + precision);
    +                fillDecimal(precision, digits, nDigits, exp, isNegative);
    +                this.decExponentRounded = exp;
    +                break;
    +            case SCIENTIFIC:
    +                exp = applyPrecision(decExp, digits, nDigits, precision + 1);
    +                fillScientific(precision, digits, nDigits, exp, isNegative);
    +                this.decExponentRounded = exp;
    +                break;
    +            case GENERAL:
    +                exp = applyPrecision(decExp, digits, nDigits, precision);
    +                // adjust precision to be the number of digits to right of decimal
    +                // the real exponent to be output is actually exp - 1, not exp
    +                if (exp - 1 < -4 || exp - 1 >= precision) {
    +                    // form = Form.SCIENTIFIC;
    +                    precision--;
    +                    fillScientific(precision, digits, nDigits, exp, isNegative);
    +                } else {
    +                    // form = Form.DECIMAL_FLOAT;
    +                    precision = precision - exp;
    +                    fillDecimal(precision, digits, nDigits, exp, isNegative);
    +                }
    +                this.decExponentRounded = exp;
    +                break;
    +            default:
    +                assert false;
    +        }
    +    }
    +
    +    // returns the exponent after rounding has been done by applyPrecision
    +    public int getExponentRounded() {
    +        return decExponentRounded - 1;
    +    }
    +
    +    public char[] getMantissa(){
    +        return mantissa;
    +    }
    +
    +    public char[] getExponent(){
    +        return exponent;
    +    }
    +
    +    /**
    +     * Returns new decExp in case of overflow.
    +     */
    +    private static int applyPrecision(int decExp, char[] digits, int nDigits, int prec) {
    +        if (prec >= nDigits || prec < 0) {
    +            // no rounding necessary
    +            return decExp;
    +        }
    +        if (prec == 0) {
    +            // only one digit (0 or 1) is returned because the precision
    +            // excludes all significant digits
    +            if (digits[0] >= '5') {
    +                digits[0] = '1';
    +                Arrays.fill(digits, 1, nDigits, '0');
    +                return decExp + 1;
    +            } else {
    +                Arrays.fill(digits, 0, nDigits, '0');
    +                return decExp;
    +            }
    +        }
    +        int q = digits[prec];
    +        if (q >= '5') {
    +            int i = prec;
    +            q = digits[--i];
    +            if ( q == '9' ) {
    +                while ( q == '9' && i > 0 ){
    +                    q = digits[--i];
    +                }
    +                if ( q == '9' ){
    +                    // carryout! High-order 1, rest 0s, larger exp.
    +                    digits[0] = '1';
    +                    Arrays.fill(digits, 1, nDigits, '0');
    +                    return decExp+1;
    +                }
    +            }
    +            digits[i] = (char)(q + 1);
    +            Arrays.fill(digits, i+1, nDigits, '0');
    +        } else {
    +            Arrays.fill(digits, prec, nDigits, '0');
    +        }
    +        return decExp;
    +    }
    +
    +    /**
    +     * Fills mantissa and exponent char arrays for compatible format.
    +     */
    +    private void fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
    +        int startIndex = isNegative ? 1 : 0;
    +        if (exp > 0 && exp < 8) {
    +            // print digits.digits.
    +            if (nDigits < exp) {
    +                int extraZeros = exp - nDigits;
    +                mantissa = create(isNegative, nDigits + extraZeros + 2);
    +                System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
    +                Arrays.fill(mantissa, startIndex + nDigits, startIndex + nDigits + extraZeros, '0');
    +                mantissa[startIndex + nDigits + extraZeros] = '.';
    +                mantissa[startIndex + nDigits + extraZeros+1] = '0';
    +            } else if (exp < nDigits) {
    +                int t = Math.min(nDigits - exp, precision);
    +                mantissa = create(isNegative, exp + 1 + t);
    +                System.arraycopy(digits, 0, mantissa, startIndex, exp);
    +                mantissa[startIndex + exp ] = '.';
    +                System.arraycopy(digits, exp, mantissa, startIndex+exp+1, t);
    +            } else { // exp == digits.length
    +                mantissa = create(isNegative, nDigits + 2);
    +                System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
    +                mantissa[startIndex + nDigits ] = '.';
    +                mantissa[startIndex + nDigits +1] = '0';
    +            }
    +        } else if (exp <= 0 && exp > -3) {
    +            int zeros = Math.max(0, Math.min(-exp, precision));
    +            int t = Math.max(0, Math.min(nDigits, precision + exp));
    +            // write '0' s before the significant digits
    +            if (zeros > 0) {
    +                mantissa = create(isNegative, zeros + 2 + t);
    +                mantissa[startIndex] = '0';
    +                mantissa[startIndex+1] = '.';
    +                Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
    +                if (t > 0) {
    +                    // copy only when significant digits are within the precision
    +                    System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
    +                }
    +            } else if (t > 0) {
    +                mantissa = create(isNegative, zeros + 2 + t);
    +                mantissa[startIndex] = '0';
    +                mantissa[startIndex + 1] = '.';
    +                // copy only when significant digits are within the precision
    +                System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
    +            } else {
    +                this.mantissa = create(isNegative, 1);
    +                this.mantissa[startIndex] = '0';
    +            }
    +        } else {
    +            if (nDigits > 1) {
    +                mantissa = create(isNegative, nDigits + 1);
    +                mantissa[startIndex] = digits[0];
    +                mantissa[startIndex + 1] = '.';
    +                System.arraycopy(digits, 1, mantissa, startIndex + 2, nDigits - 1);
    +            } else {
    +                mantissa = create(isNegative, 3);
    +                mantissa[startIndex] = digits[0];
    +                mantissa[startIndex + 1] = '.';
    +                mantissa[startIndex + 2] = '0';
    +            }
    +            int e, expStartIntex;
    +            boolean isNegExp = (exp <= 0);
    +            if (isNegExp) {
    +                e = -exp + 1;
    +                expStartIntex = 1;
    +            } else {
    +                e = exp - 1;
    +                expStartIntex = 0;
    +            }
    +            // decExponent has 1, 2, or 3, digits
    +            if (e <= 9) {
    +                exponent = create(isNegExp,1);
    +                exponent[expStartIntex] = (char) (e + '0');
    +            } else if (e <= 99) {
    +                exponent = create(isNegExp,2);
    +                exponent[expStartIntex] = (char) (e / 10 + '0');
    +                exponent[expStartIntex+1] = (char) (e % 10 + '0');
    +            } else {
    +                exponent = create(isNegExp,3);
    +                exponent[expStartIntex] = (char) (e / 100 + '0');
    +                e %= 100;
    +                exponent[expStartIntex+1] = (char) (e / 10 + '0');
    +                exponent[expStartIntex+2] = (char) (e % 10 + '0');
    +            }
    +        }
    +    }
    +
    +    private static char[] create(boolean isNegative, int size) {
    +        if(isNegative) {
    +            char[] r = new char[size +1];
    +            r[0] = '-';
    +            return r;
    +        } else {
    +            return new char[size];
    +        }
    +    }
    +
    +    /*
    +     * Fills mantissa char arrays for DECIMAL_FLOAT format.
    +     * Exponent should be equal to null.
    +     */
    +    private void fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
    +        int startIndex = isNegative ? 1 : 0;
    +        if (exp > 0) {
    +            // print digits.digits.
    +            if (nDigits < exp) {
    +                mantissa = create(isNegative,exp);
    +                System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
    +                Arrays.fill(mantissa, startIndex + nDigits, startIndex + exp, '0');
    +                // Do not append ".0" for formatted floats since the user
    +                // may request that it be omitted. It is added as necessary
    +                // by the Formatter.
    +            } else {
    +                int t = Math.min(nDigits - exp, precision);
    +                mantissa = create(isNegative, exp + (t > 0 ? (t + 1) : 0));
    +                System.arraycopy(digits, 0, mantissa, startIndex, exp);
    +                // Do not append ".0" for formatted floats since the user
    +                // may request that it be omitted. It is added as necessary
    +                // by the Formatter.
    +                if (t > 0) {
    +                    mantissa[startIndex + exp] = '.';
    +                    System.arraycopy(digits, exp, mantissa, startIndex + exp + 1, t);
    +                }
    +            }
    +        } else if (exp <= 0) {
    +            int zeros = Math.max(0, Math.min(-exp, precision));
    +            int t = Math.max(0, Math.min(nDigits, precision + exp));
    +            // write '0' s before the significant digits
    +            if (zeros > 0) {
    +                mantissa = create(isNegative, zeros + 2 + t);
    +                mantissa[startIndex] = '0';
    +                mantissa[startIndex+1] = '.';
    +                Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
    +                if (t > 0) {
    +                    // copy only when significant digits are within the precision
    +                    System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
    +                }
    +            } else if (t > 0) {
    +                mantissa = create(isNegative, zeros + 2 + t);
    +                mantissa[startIndex] = '0';
    +                mantissa[startIndex + 1] = '.';
    +                // copy only when significant digits are within the precision
    +                System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
    +            } else {
    +                this.mantissa = create(isNegative, 1);
    +                this.mantissa[startIndex] = '0';
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Fills mantissa and exponent char arrays for SCIENTIFIC format.
    +     */
    +    private void fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
    +        int startIndex = isNegative ? 1 : 0;
    +        int t = Math.max(0, Math.min(nDigits - 1, precision));
    +        if (t > 0) {
    +            mantissa = create(isNegative, t + 2);
    +            mantissa[startIndex] = digits[0];
    +            mantissa[startIndex + 1] = '.';
    +            System.arraycopy(digits, 1, mantissa, startIndex + 2, t);
    +        } else {
    +            mantissa = create(isNegative, 1);
    +            mantissa[startIndex] = digits[0];
    +        }
    +        char expSign;
    +        int e;
    +        if (exp <= 0) {
    +            expSign = '-';
    +            e = -exp + 1;
    +        } else {
    +            expSign = '+' ;
    +            e = exp - 1;
    +        }
    +        // decExponent has 1, 2, or 3, digits
    +        if (e <= 9) {
    +            exponent = new char[] { expSign,
    +                    '0', (char) (e + '0') };
    +        } else if (e <= 99) {
    +            exponent = new char[] { expSign,
    +                    (char) (e / 10 + '0'), (char) (e % 10 + '0') };
    +        } else {
    +            char hiExpChar = (char) (e / 100 + '0');
    +            e %= 100;
    +            exponent = new char[] { expSign,
    +                    hiExpChar, (char) (e / 10 + '0'), (char) (e % 10 + '0') };
    +        }
    +    }
    +}
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java
    --- a/jdk/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -39,7 +39,6 @@
     import java.util.function.Function;
     
     import sun.misc.InnocuousThread;
    -import sun.misc.ManagedLocalsThread;
     
     /**
      * CleanerImpl manages a set of object references and corresponding cleaning actions.
    @@ -130,8 +129,8 @@
          */
         public void run() {
             Thread t = Thread.currentThread();
    -        ManagedLocalsThread mlThread = (t instanceof ManagedLocalsThread)
    -                ? (ManagedLocalsThread) t
    +        InnocuousThread mlThread = (t instanceof InnocuousThread)
    +                ? (InnocuousThread) t
                     : null;
             while (!phantomCleanableList.isListEmpty() ||
                     !weakCleanableList.isListEmpty() ||
    @@ -787,4 +786,3 @@
         }
     
     }
    -
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/misc/CompoundEnumeration.java
    --- a/jdk/src/java.base/share/classes/sun/misc/CompoundEnumeration.java	Wed Dec 23 15:41:50 2015 -0800
    +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    @@ -1,63 +0,0 @@
    -/*
    - * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
    - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    - *
    - * This code is free software; you can redistribute it and/or modify it
    - * 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 sun.misc;
    -
    -import java.util.Enumeration;
    -import java.util.NoSuchElementException;
    -
    -/*
    - * A useful utility class that will enumerate over an array of
    - * enumerations.
    - */
    -public class CompoundEnumeration implements Enumeration {
    -    private Enumeration[] enums;
    -    private int index = 0;
    -
    -    public CompoundEnumeration(Enumeration[] enums) {
    -        this.enums = enums;
    -    }
    -
    -    private boolean next() {
    -        while (index < enums.length) {
    -            if (enums[index] != null && enums[index].hasMoreElements()) {
    -                return true;
    -            }
    -            index++;
    -        }
    -        return false;
    -    }
    -
    -    public boolean hasMoreElements() {
    -        return next();
    -    }
    -
    -    public E nextElement() {
    -        if (!next()) {
    -            throw new NoSuchElementException();
    -        }
    -        return enums[index].nextElement();
    -    }
    -}
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/misc/DoubleConsts.java
    --- a/jdk/src/java.base/share/classes/sun/misc/DoubleConsts.java	Wed Dec 23 15:41:50 2015 -0800
    +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    @@ -1,115 +0,0 @@
    -/*
    - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
    - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    - *
    - * This code is free software; you can redistribute it and/or modify it
    - * under the terms of the GNU General Public License version 2 only, as
    - * published by the Free Software Foundation.  Oracle designates this
    - * particular file as subject to the "Classpath" exception as provided
    - * by Oracle in the LICENSE file that accompanied this code.
    - *
    - * This code is distributed in the hope that it will be useful, but WITHOUT
    - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    - * version 2 for more details (a copy is included in the LICENSE file that
    - * accompanied this code).
    - *
    - * You should have received a copy of the GNU General Public License version
    - * 2 along with this work; if not, write to the Free Software Foundation,
    - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    - *
    - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    - * or visit www.oracle.com if you need additional information or have any
    - * questions.
    - */
    -
    -package sun.misc;
    -
    -/**
    - * This class contains additional constants documenting limits of the
    - * double type.
    - *
    - * @author Joseph D. Darcy
    - */
    -
    -public class DoubleConsts {
    -    /**
    -     * Don't let anyone instantiate this class.
    -     */
    -    private DoubleConsts() {}
    -
    -    public static final double POSITIVE_INFINITY = java.lang.Double.POSITIVE_INFINITY;
    -    public static final double NEGATIVE_INFINITY = java.lang.Double.NEGATIVE_INFINITY;
    -    public static final double NaN = java.lang.Double.NaN;
    -    public static final double MAX_VALUE = java.lang.Double.MAX_VALUE;
    -    public static final double MIN_VALUE = java.lang.Double.MIN_VALUE;
    -
    -    /**
    -     * A constant holding the smallest positive normal value of type
    -     * double, 2-1022.  It is equal to the
    -     * value returned by
    -     * Double.longBitsToDouble(0x0010000000000000L).
    -     *
    -     * @since 1.5
    -     */
    -    public static final double  MIN_NORMAL      = 2.2250738585072014E-308;
    -
    -
    -    /**
    -     * The number of logical bits in the significand of a
    -     * double number, including the implicit bit.
    -     */
    -    public static final int SIGNIFICAND_WIDTH   = 53;
    -
    -    /**
    -     * Maximum exponent a finite double number may have.
    -     * It is equal to the value returned by
    -     * Math.ilogb(Double.MAX_VALUE).
    -     */
    -    public static final int     MAX_EXPONENT    = 1023;
    -
    -    /**
    -     * Minimum exponent a normalized double number may
    -     * have.  It is equal to the value returned by
    -     * Math.ilogb(Double.MIN_NORMAL).
    -     */
    -    public static final int     MIN_EXPONENT    = -1022;
    -
    -    /**
    -     * The exponent the smallest positive double
    -     * subnormal value would have if it could be normalized..
    -     */
    -    public static final int     MIN_SUB_EXPONENT = MIN_EXPONENT -
    -                                                   (SIGNIFICAND_WIDTH - 1);
    -
    -    /**
    -     * Bias used in representing a double exponent.
    -     */
    -    public static final int     EXP_BIAS        = 1023;
    -
    -    /**
    -     * Bit mask to isolate the sign bit of a double.
    -     */
    -    public static final long    SIGN_BIT_MASK   = 0x8000000000000000L;
    -
    -    /**
    -     * Bit mask to isolate the exponent field of a
    -     * double.
    -     */
    -    public static final long    EXP_BIT_MASK    = 0x7FF0000000000000L;
    -
    -    /**
    -     * Bit mask to isolate the significand field of a
    -     * double.
    -     */
    -    public static final long    SIGNIF_BIT_MASK = 0x000FFFFFFFFFFFFFL;
    -
    -    static {
    -        // verify bit masks cover all bit positions and that the bit
    -        // masks are non-overlapping
    -        assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0L) &&
    -               (((SIGN_BIT_MASK & EXP_BIT_MASK) == 0L) &&
    -                ((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0L) &&
    -                ((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0L)));
    -    }
    -}
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/misc/FDBigInteger.java
    --- a/jdk/src/java.base/share/classes/sun/misc/FDBigInteger.java	Wed Dec 23 15:41:50 2015 -0800
    +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1508 +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 sun.misc;
    -
    -import java.math.BigInteger;
    -import java.util.Arrays;
    -//@ model import org.jmlspecs.models.JMLMath;
    -
    -/**
    - * A simple big integer package specifically for floating point base conversion.
    - */
    -public /*@ spec_bigint_math @*/ class FDBigInteger {
    -
    -    //
    -    // This class contains many comments that start with "/*@" mark.
    -    // They are behavourial specification in
    -    // the Java Modelling Language (JML):
    -    // http://www.eecs.ucf.edu/~leavens/JML//index.shtml
    -    //
    -
    -    /*@
    -    @ public pure model static \bigint UNSIGNED(int v) {
    -    @     return v >= 0 ? v : v + (((\bigint)1) << 32);
    -    @ }
    -    @
    -    @ public pure model static \bigint UNSIGNED(long v) {
    -    @     return v >= 0 ? v : v + (((\bigint)1) << 64);
    -    @ }
    -    @
    -    @ public pure model static \bigint AP(int[] data, int len) {
    -    @     return (\sum int i; 0 <= 0 && i < len; UNSIGNED(data[i]) << (i*32));
    -    @ }
    -    @
    -    @ public pure model static \bigint pow52(int p5, int p2) {
    -    @     ghost \bigint v = 1;
    -    @     for (int i = 0; i < p5; i++) v *= 5;
    -    @     return v << p2;
    -    @ }
    -    @
    -    @ public pure model static \bigint pow10(int p10) {
    -    @     return pow52(p10, p10);
    -    @ }
    -    @*/
    -
    -    static final int[] SMALL_5_POW = {
    -            1,
    -            5,
    -            5 * 5,
    -            5 * 5 * 5,
    -            5 * 5 * 5 * 5,
    -            5 * 5 * 5 * 5 * 5,
    -            5 * 5 * 5 * 5 * 5 * 5,
    -            5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
    -    };
    -
    -    static final long[] LONG_5_POW = {
    -            1L,
    -            5L,
    -            5L * 5,
    -            5L * 5 * 5,
    -            5L * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -            5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
    -    };
    -
    -    // Maximum size of cache of powers of 5 as FDBigIntegers.
    -    private static final int MAX_FIVE_POW = 340;
    -
    -    // Cache of big powers of 5 as FDBigIntegers.
    -    private static final FDBigInteger POW_5_CACHE[];
    -
    -    // Initialize FDBigInteger cache of powers of 5.
    -    static {
    -        POW_5_CACHE = new FDBigInteger[MAX_FIVE_POW];
    -        int i = 0;
    -        while (i < SMALL_5_POW.length) {
    -            FDBigInteger pow5 = new FDBigInteger(new int[]{SMALL_5_POW[i]}, 0);
    -            pow5.makeImmutable();
    -            POW_5_CACHE[i] = pow5;
    -            i++;
    -        }
    -        FDBigInteger prev = POW_5_CACHE[i - 1];
    -        while (i < MAX_FIVE_POW) {
    -            POW_5_CACHE[i] = prev = prev.mult(5);
    -            prev.makeImmutable();
    -            i++;
    -        }
    -    }
    -
    -    // Zero as an FDBigInteger.
    -    public static final FDBigInteger ZERO = new FDBigInteger(new int[0], 0);
    -
    -    // Ensure ZERO is immutable.
    -    static {
    -        ZERO.makeImmutable();
    -    }
    -
    -    // Constant for casting an int to a long via bitwise AND.
    -    private static final long LONG_MASK = 0xffffffffL;
    -
    -    //@ spec_public non_null;
    -    private int data[];  // value: data[0] is least significant
    -    //@ spec_public;
    -    private int offset;  // number of least significant zero padding ints
    -    //@ spec_public;
    -    private int nWords;  // data[nWords-1]!=0, all values above are zero
    -                 // if nWords==0 -> this FDBigInteger is zero
    -    //@ spec_public;
    -    private boolean isImmutable = false;
    -
    -    /*@
    -     @ public invariant 0 <= nWords && nWords <= data.length && offset >= 0;
    -     @ public invariant nWords == 0 ==> offset == 0;
    -     @ public invariant nWords > 0 ==> data[nWords - 1] != 0;
    -     @ public invariant (\forall int i; nWords <= i && i < data.length; data[i] == 0);
    -     @ public pure model \bigint value() {
    -     @     return AP(data, nWords) << (offset*32);
    -     @ }
    -     @*/
    -
    -    /**
    -     * Constructs an FDBigInteger from data and padding. The
    -     * data parameter has the least significant int at
    -     * the zeroth index. The offset parameter gives the number of
    -     * zero ints to be inferred below the least significant element
    -     * of data.
    -     *
    -     * @param data An array containing all non-zero ints of the value.
    -     * @param offset An offset indicating the number of zero ints to pad
    -     * below the least significant element of data.
    -     */
    -    /*@
    -     @ requires data != null && offset >= 0;
    -     @ ensures this.value() == \old(AP(data, data.length) << (offset*32));
    -     @ ensures this.data == \old(data);
    -     @*/
    -    private FDBigInteger(int[] data, int offset) {
    -        this.data = data;
    -        this.offset = offset;
    -        this.nWords = data.length;
    -        trimLeadingZeros();
    -    }
    -
    -    /**
    -     * Constructs an FDBigInteger from a starting value and some
    -     * decimal digits.
    -     *
    -     * @param lValue The starting value.
    -     * @param digits The decimal digits.
    -     * @param kDigits The initial index into digits.
    -     * @param nDigits The final index into digits.
    -     */
    -    /*@
    -     @ requires digits != null;
    -     @ requires 0 <= kDigits && kDigits <= nDigits && nDigits <= digits.length;
    -     @ requires (\forall int i; 0 <= i && i < nDigits; '0' <= digits[i] && digits[i] <= '9');
    -     @ ensures this.value() == \old(lValue * pow10(nDigits - kDigits) + (\sum int i; kDigits <= i && i < nDigits; (digits[i] - '0') * pow10(nDigits - i - 1)));
    -     @*/
    -    public FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits) {
    -        int n = Math.max((nDigits + 8) / 9, 2);        // estimate size needed.
    -        data = new int[n];      // allocate enough space
    -        data[0] = (int) lValue;    // starting value
    -        data[1] = (int) (lValue >>> 32);
    -        offset = 0;
    -        nWords = 2;
    -        int i = kDigits;
    -        int limit = nDigits - 5;       // slurp digits 5 at a time.
    -        int v;
    -        while (i < limit) {
    -            int ilim = i + 5;
    -            v = (int) digits[i++] - (int) '0';
    -            while (i < ilim) {
    -                v = 10 * v + (int) digits[i++] - (int) '0';
    -            }
    -            multAddMe(100000, v); // ... where 100000 is 10^5.
    -        }
    -        int factor = 1;
    -        v = 0;
    -        while (i < nDigits) {
    -            v = 10 * v + (int) digits[i++] - (int) '0';
    -            factor *= 10;
    -        }
    -        if (factor != 1) {
    -            multAddMe(factor, v);
    -        }
    -        trimLeadingZeros();
    -    }
    -
    -    /**
    -     * Returns an FDBigInteger with the numerical value
    -     * 5p5 * 2p2.
    -     *
    -     * @param p5 The exponent of the power-of-five factor.
    -     * @param p2 The exponent of the power-of-two factor.
    -     * @return 5p5 * 2p2
    -     */
    -    /*@
    -     @ requires p5 >= 0 && p2 >= 0;
    -     @ assignable \nothing;
    -     @ ensures \result.value() == \old(pow52(p5, p2));
    -     @*/
    -    public static FDBigInteger valueOfPow52(int p5, int p2) {
    -        if (p5 != 0) {
    -            if (p2 == 0) {
    -                return big5pow(p5);
    -            } else if (p5 < SMALL_5_POW.length) {
    -                int pow5 = SMALL_5_POW[p5];
    -                int wordcount = p2 >> 5;
    -                int bitcount = p2 & 0x1f;
    -                if (bitcount == 0) {
    -                    return new FDBigInteger(new int[]{pow5}, wordcount);
    -                } else {
    -                    return new FDBigInteger(new int[]{
    -                            pow5 << bitcount,
    -                            pow5 >>> (32 - bitcount)
    -                    }, wordcount);
    -                }
    -            } else {
    -                return big5pow(p5).leftShift(p2);
    -            }
    -        } else {
    -            return valueOfPow2(p2);
    -        }
    -    }
    -
    -    /**
    -     * Returns an FDBigInteger with the numerical value
    -     * value * 5p5 * 2p2.
    -     *
    -     * @param value The constant factor.
    -     * @param p5 The exponent of the power-of-five factor.
    -     * @param p2 The exponent of the power-of-two factor.
    -     * @return value * 5p5 * 2p2
    -     */
    -    /*@
    -     @ requires p5 >= 0 && p2 >= 0;
    -     @ assignable \nothing;
    -     @ ensures \result.value() == \old(UNSIGNED(value) * pow52(p5, p2));
    -     @*/
    -    public static FDBigInteger valueOfMulPow52(long value, int p5, int p2) {
    -        assert p5 >= 0 : p5;
    -        assert p2 >= 0 : p2;
    -        int v0 = (int) value;
    -        int v1 = (int) (value >>> 32);
    -        int wordcount = p2 >> 5;
    -        int bitcount = p2 & 0x1f;
    -        if (p5 != 0) {
    -            if (p5 < SMALL_5_POW.length) {
    -                long pow5 = SMALL_5_POW[p5] & LONG_MASK;
    -                long carry = (v0 & LONG_MASK) * pow5;
    -                v0 = (int) carry;
    -                carry >>>= 32;
    -                carry = (v1 & LONG_MASK) * pow5 + carry;
    -                v1 = (int) carry;
    -                int v2 = (int) (carry >>> 32);
    -                if (bitcount == 0) {
    -                    return new FDBigInteger(new int[]{v0, v1, v2}, wordcount);
    -                } else {
    -                    return new FDBigInteger(new int[]{
    -                            v0 << bitcount,
    -                            (v1 << bitcount) | (v0 >>> (32 - bitcount)),
    -                            (v2 << bitcount) | (v1 >>> (32 - bitcount)),
    -                            v2 >>> (32 - bitcount)
    -                    }, wordcount);
    -                }
    -            } else {
    -                FDBigInteger pow5 = big5pow(p5);
    -                int[] r;
    -                if (v1 == 0) {
    -                    r = new int[pow5.nWords + 1 + ((p2 != 0) ? 1 : 0)];
    -                    mult(pow5.data, pow5.nWords, v0, r);
    -                } else {
    -                    r = new int[pow5.nWords + 2 + ((p2 != 0) ? 1 : 0)];
    -                    mult(pow5.data, pow5.nWords, v0, v1, r);
    -                }
    -                return (new FDBigInteger(r, pow5.offset)).leftShift(p2);
    -            }
    -        } else if (p2 != 0) {
    -            if (bitcount == 0) {
    -                return new FDBigInteger(new int[]{v0, v1}, wordcount);
    -            } else {
    -                return new FDBigInteger(new int[]{
    -                         v0 << bitcount,
    -                        (v1 << bitcount) | (v0 >>> (32 - bitcount)),
    -                        v1 >>> (32 - bitcount)
    -                }, wordcount);
    -            }
    -        }
    -        return new FDBigInteger(new int[]{v0, v1}, 0);
    -    }
    -
    -    /**
    -     * Returns an FDBigInteger with the numerical value
    -     * 2p2.
    -     *
    -     * @param p2 The exponent of 2.
    -     * @return 2p2
    -     */
    -    /*@
    -     @ requires p2 >= 0;
    -     @ assignable \nothing;
    -     @ ensures \result.value() == pow52(0, p2);
    -     @*/
    -    private static FDBigInteger valueOfPow2(int p2) {
    -        int wordcount = p2 >> 5;
    -        int bitcount = p2 & 0x1f;
    -        return new FDBigInteger(new int[]{1 << bitcount}, wordcount);
    -    }
    -
    -    /**
    -     * Removes all leading zeros from this FDBigInteger adjusting
    -     * the offset and number of non-zero leading words accordingly.
    -     */
    -    /*@
    -     @ requires data != null;
    -     @ requires 0 <= nWords && nWords <= data.length && offset >= 0;
    -     @ requires nWords == 0 ==> offset == 0;
    -     @ ensures nWords == 0 ==> offset == 0;
    -     @ ensures nWords > 0 ==> data[nWords - 1] != 0;
    -     @*/
    -    private /*@ helper @*/ void trimLeadingZeros() {
    -        int i = nWords;
    -        if (i > 0 && (data[--i] == 0)) {
    -            //for (; i > 0 && data[i - 1] == 0; i--) ;
    -            while(i > 0 && data[i - 1] == 0) {
    -                i--;
    -            }
    -            this.nWords = i;
    -            if (i == 0) { // all words are zero
    -                this.offset = 0;
    -            }
    -        }
    -    }
    -
    -    /**
    -     * Retrieves the normalization bias of the FDBigIntger. The
    -     * normalization bias is a left shift such that after it the highest word
    -     * of the value will have the 4 highest bits equal to zero:
    -     * {@code (highestWord & 0xf0000000) == 0}, but the next bit should be 1
    -     * {@code (highestWord & 0x08000000) != 0}.
    -     *
    -     * @return The normalization bias.
    -     */
    -    /*@
    -     @ requires this.value() > 0;
    -     @*/
    -    public /*@ pure @*/ int getNormalizationBias() {
    -        if (nWords == 0) {
    -            throw new IllegalArgumentException("Zero value cannot be normalized");
    -        }
    -        int zeros = Integer.numberOfLeadingZeros(data[nWords - 1]);
    -        return (zeros < 4) ? 28 + zeros : zeros - 4;
    -    }
    -
    -    // TODO: Why is anticount param needed if it is always 32 - bitcount?
    -    /**
    -     * Left shifts the contents of one int array into another.
    -     *
    -     * @param src The source array.
    -     * @param idx The initial index of the source array.
    -     * @param result The destination array.
    -     * @param bitcount The left shift.
    -     * @param anticount The left anti-shift, e.g., 32-bitcount.
    -     * @param prev The prior source value.
    -     */
    -    /*@
    -     @ requires 0 < bitcount && bitcount < 32 && anticount == 32 - bitcount;
    -     @ requires src.length >= idx && result.length > idx;
    -     @ assignable result[*];
    -     @ ensures AP(result, \old(idx + 1)) == \old((AP(src, idx) + UNSIGNED(prev) << (idx*32)) << bitcount);
    -     @*/
    -    private static void leftShift(int[] src, int idx, int result[], int bitcount, int anticount, int prev){
    -        for (; idx > 0; idx--) {
    -            int v = (prev << bitcount);
    -            prev = src[idx - 1];
    -            v |= (prev >>> anticount);
    -            result[idx] = v;
    -        }
    -        int v = prev << bitcount;
    -        result[0] = v;
    -    }
    -
    -    /**
    -     * Shifts this FDBigInteger to the left. The shift is performed
    -     * in-place unless the FDBigInteger is immutable in which case
    -     * a new instance of FDBigInteger is returned.
    -     *
    -     * @param shift The number of bits to shift left.
    -     * @return The shifted FDBigInteger.
    -     */
    -    /*@
    -     @ requires this.value() == 0 || shift == 0;
    -     @ assignable \nothing;
    -     @ ensures \result == this;
    -     @
    -     @  also
    -     @
    -     @ requires this.value() > 0 && shift > 0 && this.isImmutable;
    -     @ assignable \nothing;
    -     @ ensures \result.value() == \old(this.value() << shift);
    -     @
    -     @  also
    -     @
    -     @ requires this.value() > 0 && shift > 0 && this.isImmutable;
    -     @ assignable \nothing;
    -     @ ensures \result == this;
    -     @ ensures \result.value() == \old(this.value() << shift);
    -     @*/
    -    public FDBigInteger leftShift(int shift) {
    -        if (shift == 0 || nWords == 0) {
    -            return this;
    -        }
    -        int wordcount = shift >> 5;
    -        int bitcount = shift & 0x1f;
    -        if (this.isImmutable) {
    -            if (bitcount == 0) {
    -                return new FDBigInteger(Arrays.copyOf(data, nWords), offset + wordcount);
    -            } else {
    -                int anticount = 32 - bitcount;
    -                int idx = nWords - 1;
    -                int prev = data[idx];
    -                int hi = prev >>> anticount;
    -                int[] result;
    -                if (hi != 0) {
    -                    result = new int[nWords + 1];
    -                    result[nWords] = hi;
    -                } else {
    -                    result = new int[nWords];
    -                }
    -                leftShift(data,idx,result,bitcount,anticount,prev);
    -                return new FDBigInteger(result, offset + wordcount);
    -            }
    -        } else {
    -            if (bitcount != 0) {
    -                int anticount = 32 - bitcount;
    -                if ((data[0] << bitcount) == 0) {
    -                    int idx = 0;
    -                    int prev = data[idx];
    -                    for (; idx < nWords - 1; idx++) {
    -                        int v = (prev >>> anticount);
    -                        prev = data[idx + 1];
    -                        v |= (prev << bitcount);
    -                        data[idx] = v;
    -                    }
    -                    int v = prev >>> anticount;
    -                    data[idx] = v;
    -                    if(v==0) {
    -                        nWords--;
    -                    }
    -                    offset++;
    -                } else {
    -                    int idx = nWords - 1;
    -                    int prev = data[idx];
    -                    int hi = prev >>> anticount;
    -                    int[] result = data;
    -                    int[] src = data;
    -                    if (hi != 0) {
    -                        if(nWords == data.length) {
    -                            data = result = new int[nWords + 1];
    -                        }
    -                        result[nWords++] = hi;
    -                    }
    -                    leftShift(src,idx,result,bitcount,anticount,prev);
    -                }
    -            }
    -            offset += wordcount;
    -            return this;
    -        }
    -    }
    -
    -    /**
    -     * Returns the number of ints this FDBigInteger represents.
    -     *
    -     * @return Number of ints required to represent this FDBigInteger.
    -     */
    -    /*@
    -     @ requires this.value() == 0;
    -     @ ensures \result == 0;
    -     @
    -     @  also
    -     @
    -     @ requires this.value() > 0;
    -     @ ensures ((\bigint)1) << (\result - 1) <= this.value() && this.value() <= ((\bigint)1) << \result;
    -     @*/
    -    private /*@ pure @*/ int size() {
    -        return nWords + offset;
    -    }
    -
    -
    -    /**
    -     * Computes
    -     * 
    -     * q = (int)( this / S )
    -     * this = 10 * ( this mod S )
    -     * Return q.
    -     * 
    - * This is the iteration step of digit development for output. - * We assume that S has been normalized, as above, and that - * "this" has been left-shifted accordingly. - * Also assumed, of course, is that the result, q, can be expressed - * as an integer, {@code 0 <= q < 10}. - * - * @param S The divisor of this FDBigInteger. - * @return q = (int)(this / S). - */ - /*@ - @ requires !this.isImmutable; - @ requires this.size() <= S.size(); - @ requires this.data.length + this.offset >= S.size(); - @ requires S.value() >= ((\bigint)1) << (S.size()*32 - 4); - @ assignable this.nWords, this.offset, this.data, this.data[*]; - @ ensures \result == \old(this.value() / S.value()); - @ ensures this.value() == \old(10 * (this.value() % S.value())); - @*/ - public int quoRemIteration(FDBigInteger S) throws IllegalArgumentException { - assert !this.isImmutable : "cannot modify immutable value"; - // ensure that this and S have the same number of - // digits. If S is properly normalized and q < 10 then - // this must be so. - int thSize = this.size(); - int sSize = S.size(); - if (thSize < sSize) { - // this value is significantly less than S, result of division is zero. - // just mult this by 10. - int p = multAndCarryBy10(this.data, this.nWords, this.data); - if(p!=0) { - this.data[nWords++] = p; - } else { - trimLeadingZeros(); - } - return 0; - } else if (thSize > sSize) { - throw new IllegalArgumentException("disparate values"); - } - // estimate q the obvious way. We will usually be - // right. If not, then we're only off by a little and - // will re-add. - long q = (this.data[this.nWords - 1] & LONG_MASK) / (S.data[S.nWords - 1] & LONG_MASK); - long diff = multDiffMe(q, S); - if (diff != 0L) { - //@ assert q != 0; - //@ assert this.offset == \old(Math.min(this.offset, S.offset)); - //@ assert this.offset <= S.offset; - - // q is too big. - // add S back in until this turns +. This should - // not be very many times! - long sum = 0L; - int tStart = S.offset - this.offset; - //@ assert tStart >= 0; - int[] sd = S.data; - int[] td = this.data; - while (sum == 0L) { - for (int sIndex = 0, tIndex = tStart; tIndex < this.nWords; sIndex++, tIndex++) { - sum += (td[tIndex] & LONG_MASK) + (sd[sIndex] & LONG_MASK); - td[tIndex] = (int) sum; - sum >>>= 32; // Signed or unsigned, answer is 0 or 1 - } - // - // Originally the following line read - // "if ( sum !=0 && sum != -1 )" - // but that would be wrong, because of the - // treatment of the two values as entirely unsigned, - // it would be impossible for a carry-out to be interpreted - // as -1 -- it would have to be a single-bit carry-out, or +1. - // - assert sum == 0 || sum == 1 : sum; // carry out of division correction - q -= 1; - } - } - // finally, we can multiply this by 10. - // it cannot overflow, right, as the high-order word has - // at least 4 high-order zeros! - int p = multAndCarryBy10(this.data, this.nWords, this.data); - assert p == 0 : p; // Carry out of *10 - trimLeadingZeros(); - return (int) q; - } - - /** - * Multiplies this FDBigInteger by 10. The operation will be - * performed in place unless the FDBigInteger is immutable in - * which case a new FDBigInteger will be returned. - * - * @return The FDBigInteger multiplied by 10. - */ - /*@ - @ requires this.value() == 0; - @ assignable \nothing; - @ ensures \result == this; - @ - @ also - @ - @ requires this.value() > 0 && this.isImmutable; - @ assignable \nothing; - @ ensures \result.value() == \old(this.value() * 10); - @ - @ also - @ - @ requires this.value() > 0 && !this.isImmutable; - @ assignable this.nWords, this.data, this.data[*]; - @ ensures \result == this; - @ ensures \result.value() == \old(this.value() * 10); - @*/ - public FDBigInteger multBy10() { - if (nWords == 0) { - return this; - } - if (isImmutable) { - int[] res = new int[nWords + 1]; - res[nWords] = multAndCarryBy10(data, nWords, res); - return new FDBigInteger(res, offset); - } else { - int p = multAndCarryBy10(this.data, this.nWords, this.data); - if (p != 0) { - if (nWords == data.length) { - if (data[0] == 0) { - System.arraycopy(data, 1, data, 0, --nWords); - offset++; - } else { - data = Arrays.copyOf(data, data.length + 1); - } - } - data[nWords++] = p; - } else { - trimLeadingZeros(); - } - return this; - } - } - - /** - * Multiplies this FDBigInteger by - * 5p5 * 2p2. The operation will be - * performed in place if possible, otherwise a new FDBigInteger - * will be returned. - * - * @param p5 The exponent of the power-of-five factor. - * @param p2 The exponent of the power-of-two factor. - * @return The multiplication result. - */ - /*@ - @ requires this.value() == 0 || p5 == 0 && p2 == 0; - @ assignable \nothing; - @ ensures \result == this; - @ - @ also - @ - @ requires this.value() > 0 && (p5 > 0 && p2 >= 0 || p5 == 0 && p2 > 0 && this.isImmutable); - @ assignable \nothing; - @ ensures \result.value() == \old(this.value() * pow52(p5, p2)); - @ - @ also - @ - @ requires this.value() > 0 && p5 == 0 && p2 > 0 && !this.isImmutable; - @ assignable this.nWords, this.data, this.data[*]; - @ ensures \result == this; - @ ensures \result.value() == \old(this.value() * pow52(p5, p2)); - @*/ - public FDBigInteger multByPow52(int p5, int p2) { - if (this.nWords == 0) { - return this; - } - FDBigInteger res = this; - if (p5 != 0) { - int[] r; - int extraSize = (p2 != 0) ? 1 : 0; - if (p5 < SMALL_5_POW.length) { - r = new int[this.nWords + 1 + extraSize]; - mult(this.data, this.nWords, SMALL_5_POW[p5], r); - res = new FDBigInteger(r, this.offset); - } else { - FDBigInteger pow5 = big5pow(p5); - r = new int[this.nWords + pow5.size() + extraSize]; - mult(this.data, this.nWords, pow5.data, pow5.nWords, r); - res = new FDBigInteger(r, this.offset + pow5.offset); - } - } - return res.leftShift(p2); - } - - /** - * Multiplies two big integers represented as int arrays. - * - * @param s1 The first array factor. - * @param s1Len The number of elements of s1 to use. - * @param s2 The second array factor. - * @param s2Len The number of elements of s2 to use. - * @param dst The product array. - */ - /*@ - @ requires s1 != dst && s2 != dst; - @ requires s1.length >= s1Len && s2.length >= s2Len && dst.length >= s1Len + s2Len; - @ assignable dst[0 .. s1Len + s2Len - 1]; - @ ensures AP(dst, s1Len + s2Len) == \old(AP(s1, s1Len) * AP(s2, s2Len)); - @*/ - private static void mult(int[] s1, int s1Len, int[] s2, int s2Len, int[] dst) { - for (int i = 0; i < s1Len; i++) { - long v = s1[i] & LONG_MASK; - long p = 0L; - for (int j = 0; j < s2Len; j++) { - p += (dst[i + j] & LONG_MASK) + v * (s2[j] & LONG_MASK); - dst[i + j] = (int) p; - p >>>= 32; - } - dst[i + s2Len] = (int) p; - } - } - - /** - * Subtracts the supplied FDBigInteger subtrahend from this - * FDBigInteger. Assert that the result is positive. - * If the subtrahend is immutable, store the result in this(minuend). - * If this(minuend) is immutable a new FDBigInteger is created. - * - * @param subtrahend The FDBigInteger to be subtracted. - * @return This FDBigInteger less the subtrahend. - */ - /*@ - @ requires this.isImmutable; - @ requires this.value() >= subtrahend.value(); - @ assignable \nothing; - @ ensures \result.value() == \old(this.value() - subtrahend.value()); - @ - @ also - @ - @ requires !subtrahend.isImmutable; - @ requires this.value() >= subtrahend.value(); - @ assignable this.nWords, this.offset, this.data, this.data[*]; - @ ensures \result == this; - @ ensures \result.value() == \old(this.value() - subtrahend.value()); - @*/ - public FDBigInteger leftInplaceSub(FDBigInteger subtrahend) { - assert this.size() >= subtrahend.size() : "result should be positive"; - FDBigInteger minuend; - if (this.isImmutable) { - minuend = new FDBigInteger(this.data.clone(), this.offset); - } else { - minuend = this; - } - int offsetDiff = subtrahend.offset - minuend.offset; - int[] sData = subtrahend.data; - int[] mData = minuend.data; - int subLen = subtrahend.nWords; - int minLen = minuend.nWords; - if (offsetDiff < 0) { - // need to expand minuend - int rLen = minLen - offsetDiff; - if (rLen < mData.length) { - System.arraycopy(mData, 0, mData, -offsetDiff, minLen); - Arrays.fill(mData, 0, -offsetDiff, 0); - } else { - int[] r = new int[rLen]; - System.arraycopy(mData, 0, r, -offsetDiff, minLen); - minuend.data = mData = r; - } - minuend.offset = subtrahend.offset; - minuend.nWords = minLen = rLen; - offsetDiff = 0; - } - long borrow = 0L; - int mIndex = offsetDiff; - for (int sIndex = 0; sIndex < subLen && mIndex < minLen; sIndex++, mIndex++) { - long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow; - mData[mIndex] = (int) diff; - borrow = diff >> 32; // signed shift - } - for (; borrow != 0 && mIndex < minLen; mIndex++) { - long diff = (mData[mIndex] & LONG_MASK) + borrow; - mData[mIndex] = (int) diff; - borrow = diff >> 32; // signed shift - } - assert borrow == 0L : borrow; // borrow out of subtract, - // result should be positive - minuend.trimLeadingZeros(); - return minuend; - } - - /** - * Subtracts the supplied FDBigInteger subtrahend from this - * FDBigInteger. Assert that the result is positive. - * If the this(minuend) is immutable, store the result in subtrahend. - * If subtrahend is immutable a new FDBigInteger is created. - * - * @param subtrahend The FDBigInteger to be subtracted. - * @return This FDBigInteger less the subtrahend. - */ - /*@ - @ requires subtrahend.isImmutable; - @ requires this.value() >= subtrahend.value(); - @ assignable \nothing; - @ ensures \result.value() == \old(this.value() - subtrahend.value()); - @ - @ also - @ - @ requires !subtrahend.isImmutable; - @ requires this.value() >= subtrahend.value(); - @ assignable subtrahend.nWords, subtrahend.offset, subtrahend.data, subtrahend.data[*]; - @ ensures \result == subtrahend; - @ ensures \result.value() == \old(this.value() - subtrahend.value()); - @*/ - public FDBigInteger rightInplaceSub(FDBigInteger subtrahend) { - assert this.size() >= subtrahend.size() : "result should be positive"; - FDBigInteger minuend = this; - if (subtrahend.isImmutable) { - subtrahend = new FDBigInteger(subtrahend.data.clone(), subtrahend.offset); - } - int offsetDiff = minuend.offset - subtrahend.offset; - int[] sData = subtrahend.data; - int[] mData = minuend.data; - int subLen = subtrahend.nWords; - int minLen = minuend.nWords; - if (offsetDiff < 0) { - int rLen = minLen; - if (rLen < sData.length) { - System.arraycopy(sData, 0, sData, -offsetDiff, subLen); - Arrays.fill(sData, 0, -offsetDiff, 0); - } else { - int[] r = new int[rLen]; - System.arraycopy(sData, 0, r, -offsetDiff, subLen); - subtrahend.data = sData = r; - } - subtrahend.offset = minuend.offset; - subLen -= offsetDiff; - offsetDiff = 0; - } else { - int rLen = minLen + offsetDiff; - if (rLen >= sData.length) { - subtrahend.data = sData = Arrays.copyOf(sData, rLen); - } - } - //@ assert minuend == this && minuend.value() == \old(this.value()); - //@ assert mData == minuend.data && minLen == minuend.nWords; - //@ assert subtrahend.offset + subtrahend.data.length >= minuend.size(); - //@ assert sData == subtrahend.data; - //@ assert AP(subtrahend.data, subtrahend.data.length) << subtrahend.offset == \old(subtrahend.value()); - //@ assert subtrahend.offset == Math.min(\old(this.offset), minuend.offset); - //@ assert offsetDiff == minuend.offset - subtrahend.offset; - //@ assert 0 <= offsetDiff && offsetDiff + minLen <= sData.length; - int sIndex = 0; - long borrow = 0L; - for (; sIndex < offsetDiff; sIndex++) { - long diff = 0L - (sData[sIndex] & LONG_MASK) + borrow; - sData[sIndex] = (int) diff; - borrow = diff >> 32; // signed shift - } - //@ assert sIndex == offsetDiff; - for (int mIndex = 0; mIndex < minLen; sIndex++, mIndex++) { - //@ assert sIndex == offsetDiff + mIndex; - long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow; - sData[sIndex] = (int) diff; - borrow = diff >> 32; // signed shift - } - assert borrow == 0L : borrow; // borrow out of subtract, - // result should be positive - subtrahend.nWords = sIndex; - subtrahend.trimLeadingZeros(); - return subtrahend; - - } - - /** - * Determines whether all elements of an array are zero for all indices less - * than a given index. - * - * @param a The array to be examined. - * @param from The index strictly below which elements are to be examined. - * @return Zero if all elements in range are zero, 1 otherwise. - */ - /*@ - @ requires 0 <= from && from <= a.length; - @ ensures \result == (AP(a, from) == 0 ? 0 : 1); - @*/ - private /*@ pure @*/ static int checkZeroTail(int[] a, int from) { - while (from > 0) { - if (a[--from] != 0) { - return 1; - } - } - return 0; - } - - /** - * Compares the parameter with this FDBigInteger. Returns an - * integer accordingly as: - *
    {@code
    -     * > 0: this > other
    -     *   0: this == other
    -     * < 0: this < other
    -     * }
    - * - * @param other The FDBigInteger to compare. - * @return A negative value, zero, or a positive value according to the - * result of the comparison. - */ - /*@ - @ ensures \result == (this.value() < other.value() ? -1 : this.value() > other.value() ? +1 : 0); - @*/ - public /*@ pure @*/ int cmp(FDBigInteger other) { - int aSize = nWords + offset; - int bSize = other.nWords + other.offset; - if (aSize > bSize) { - return 1; - } else if (aSize < bSize) { - return -1; - } - int aLen = nWords; - int bLen = other.nWords; - while (aLen > 0 && bLen > 0) { - int a = data[--aLen]; - int b = other.data[--bLen]; - if (a != b) { - return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1; - } - } - if (aLen > 0) { - return checkZeroTail(data, aLen); - } - if (bLen > 0) { - return -checkZeroTail(other.data, bLen); - } - return 0; - } - - /** - * Compares this FDBigInteger with - * 5p5 * 2p2. - * Returns an integer accordingly as: - *
    {@code
    -     * > 0: this > other
    -     *   0: this == other
    -     * < 0: this < other
    -     * }
    - * @param p5 The exponent of the power-of-five factor. - * @param p2 The exponent of the power-of-two factor. - * @return A negative value, zero, or a positive value according to the - * result of the comparison. - */ - /*@ - @ requires p5 >= 0 && p2 >= 0; - @ ensures \result == (this.value() < pow52(p5, p2) ? -1 : this.value() > pow52(p5, p2) ? +1 : 0); - @*/ - public /*@ pure @*/ int cmpPow52(int p5, int p2) { - if (p5 == 0) { - int wordcount = p2 >> 5; - int bitcount = p2 & 0x1f; - int size = this.nWords + this.offset; - if (size > wordcount + 1) { - return 1; - } else if (size < wordcount + 1) { - return -1; - } - int a = this.data[this.nWords -1]; - int b = 1 << bitcount; - if (a != b) { - return ( (a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1; - } - return checkZeroTail(this.data, this.nWords - 1); - } - return this.cmp(big5pow(p5).leftShift(p2)); - } - - /** - * Compares this FDBigInteger with x + y. Returns a - * value according to the comparison as: - *
    {@code
    -     * -1: this <  x + y
    -     *  0: this == x + y
    -     *  1: this >  x + y
    -     * }
    - * @param x The first addend of the sum to compare. - * @param y The second addend of the sum to compare. - * @return -1, 0, or 1 according to the result of the comparison. - */ - /*@ - @ ensures \result == (this.value() < x.value() + y.value() ? -1 : this.value() > x.value() + y.value() ? +1 : 0); - @*/ - public /*@ pure @*/ int addAndCmp(FDBigInteger x, FDBigInteger y) { - FDBigInteger big; - FDBigInteger small; - int xSize = x.size(); - int ySize = y.size(); - int bSize; - int sSize; - if (xSize >= ySize) { - big = x; - small = y; - bSize = xSize; - sSize = ySize; - } else { - big = y; - small = x; - bSize = ySize; - sSize = xSize; - } - int thSize = this.size(); - if (bSize == 0) { - return thSize == 0 ? 0 : 1; - } - if (sSize == 0) { - return this.cmp(big); - } - if (bSize > thSize) { - return -1; - } - if (bSize + 1 < thSize) { - return 1; - } - long top = (big.data[big.nWords - 1] & LONG_MASK); - if (sSize == bSize) { - top += (small.data[small.nWords - 1] & LONG_MASK); - } - if ((top >>> 32) == 0) { - if (((top + 1) >>> 32) == 0) { - // good case - no carry extension - if (bSize < thSize) { - return 1; - } - // here sum.nWords == this.nWords - long v = (this.data[this.nWords - 1] & LONG_MASK); - if (v < top) { - return -1; - } - if (v > top + 1) { - return 1; - } - } - } else { // (top>>>32)!=0 guaranteed carry extension - if (bSize + 1 > thSize) { - return -1; - } - // here sum.nWords == this.nWords - top >>>= 32; - long v = (this.data[this.nWords - 1] & LONG_MASK); - if (v < top) { - return -1; - } - if (v > top + 1) { - return 1; - } - } - return this.cmp(big.add(small)); - } - - /** - * Makes this FDBigInteger immutable. - */ - /*@ - @ assignable this.isImmutable; - @ ensures this.isImmutable; - @*/ - public void makeImmutable() { - this.isImmutable = true; - } - - /** - * Multiplies this FDBigInteger by an integer. - * - * @param i The factor by which to multiply this FDBigInteger. - * @return This FDBigInteger multiplied by an integer. - */ - /*@ - @ requires this.value() == 0; - @ assignable \nothing; - @ ensures \result == this; - @ - @ also - @ - @ requires this.value() != 0; - @ assignable \nothing; - @ ensures \result.value() == \old(this.value() * UNSIGNED(i)); - @*/ - private FDBigInteger mult(int i) { - if (this.nWords == 0) { - return this; - } - int[] r = new int[nWords + 1]; - mult(data, nWords, i, r); - return new FDBigInteger(r, offset); - } - - /** - * Multiplies this FDBigInteger by another FDBigInteger. - * - * @param other The FDBigInteger factor by which to multiply. - * @return The product of this and the parameter FDBigIntegers. - */ - /*@ - @ requires this.value() == 0; - @ assignable \nothing; - @ ensures \result == this; - @ - @ also - @ - @ requires this.value() != 0 && other.value() == 0; - @ assignable \nothing; - @ ensures \result == other; - @ - @ also - @ - @ requires this.value() != 0 && other.value() != 0; - @ assignable \nothing; - @ ensures \result.value() == \old(this.value() * other.value()); - @*/ - private FDBigInteger mult(FDBigInteger other) { - if (this.nWords == 0) { - return this; - } - if (this.size() == 1) { - return other.mult(data[0]); - } - if (other.nWords == 0) { - return other; - } - if (other.size() == 1) { - return this.mult(other.data[0]); - } - int[] r = new int[nWords + other.nWords]; - mult(this.data, this.nWords, other.data, other.nWords, r); - return new FDBigInteger(r, this.offset + other.offset); - } - - /** - * Adds another FDBigInteger to this FDBigInteger. - * - * @param other The FDBigInteger to add. - * @return The sum of the FDBigIntegers. - */ - /*@ - @ assignable \nothing; - @ ensures \result.value() == \old(this.value() + other.value()); - @*/ - private FDBigInteger add(FDBigInteger other) { - FDBigInteger big, small; - int bigLen, smallLen; - int tSize = this.size(); - int oSize = other.size(); - if (tSize >= oSize) { - big = this; - bigLen = tSize; - small = other; - smallLen = oSize; - } else { - big = other; - bigLen = oSize; - small = this; - smallLen = tSize; - } - int[] r = new int[bigLen + 1]; - int i = 0; - long carry = 0L; - for (; i < smallLen; i++) { - carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) ) - + ((i < small.offset ? 0L : (small.data[i - small.offset] & LONG_MASK))); - r[i] = (int) carry; - carry >>= 32; // signed shift. - } - for (; i < bigLen; i++) { - carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) ); - r[i] = (int) carry; - carry >>= 32; // signed shift. - } - r[bigLen] = (int) carry; - return new FDBigInteger(r, 0); - } - - - /** - * Multiplies a FDBigInteger by an int and adds another int. The - * result is computed in place. This method is intended only to be invoked - * from - * - * FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits) - * . - * - * @param iv The factor by which to multiply this FDBigInteger. - * @param addend The value to add to the product of this - * FDBigInteger and iv. - */ - /*@ - @ requires this.value()*UNSIGNED(iv) + UNSIGNED(addend) < ((\bigint)1) << ((this.data.length + this.offset)*32); - @ assignable this.data[*]; - @ ensures this.value() == \old(this.value()*UNSIGNED(iv) + UNSIGNED(addend)); - @*/ - private /*@ helper @*/ void multAddMe(int iv, int addend) { - long v = iv & LONG_MASK; - // unroll 0th iteration, doing addition. - long p = v * (data[0] & LONG_MASK) + (addend & LONG_MASK); - data[0] = (int) p; - p >>>= 32; - for (int i = 1; i < nWords; i++) { - p += v * (data[i] & LONG_MASK); - data[i] = (int) p; - p >>>= 32; - } - if (p != 0L) { - data[nWords++] = (int) p; // will fail noisily if illegal! - } - } - - // - // original doc: - // - // do this -=q*S - // returns borrow - // - /** - * Multiplies the parameters and subtracts them from this - * FDBigInteger. - * - * @param q The integer parameter. - * @param S The FDBigInteger parameter. - * @return this - q*S. - */ - /*@ - @ ensures nWords == 0 ==> offset == 0; - @ ensures nWords > 0 ==> data[nWords - 1] != 0; - @*/ - /*@ - @ requires 0 < q && q <= (1L << 31); - @ requires data != null; - @ requires 0 <= nWords && nWords <= data.length && offset >= 0; - @ requires !this.isImmutable; - @ requires this.size() == S.size(); - @ requires this != S; - @ assignable this.nWords, this.offset, this.data, this.data[*]; - @ ensures -q <= \result && \result <= 0; - @ ensures this.size() == \old(this.size()); - @ ensures this.value() + (\result << (this.size()*32)) == \old(this.value() - q*S.value()); - @ ensures this.offset == \old(Math.min(this.offset, S.offset)); - @ ensures \old(this.offset <= S.offset) ==> this.nWords == \old(this.nWords); - @ ensures \old(this.offset <= S.offset) ==> this.offset == \old(this.offset); - @ ensures \old(this.offset <= S.offset) ==> this.data == \old(this.data); - @ - @ also - @ - @ requires q == 0; - @ assignable \nothing; - @ ensures \result == 0; - @*/ - private /*@ helper @*/ long multDiffMe(long q, FDBigInteger S) { - long diff = 0L; - if (q != 0) { - int deltaSize = S.offset - this.offset; - if (deltaSize >= 0) { - int[] sd = S.data; - int[] td = this.data; - for (int sIndex = 0, tIndex = deltaSize; sIndex < S.nWords; sIndex++, tIndex++) { - diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK); - td[tIndex] = (int) diff; - diff >>= 32; // N.B. SIGNED shift. - } - } else { - deltaSize = -deltaSize; - int[] rd = new int[nWords + deltaSize]; - int sIndex = 0; - int rIndex = 0; - int[] sd = S.data; - for (; rIndex < deltaSize && sIndex < S.nWords; sIndex++, rIndex++) { - diff -= q * (sd[sIndex] & LONG_MASK); - rd[rIndex] = (int) diff; - diff >>= 32; // N.B. SIGNED shift. - } - int tIndex = 0; - int[] td = this.data; - for (; sIndex < S.nWords; sIndex++, tIndex++, rIndex++) { - diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK); - rd[rIndex] = (int) diff; - diff >>= 32; // N.B. SIGNED shift. - } - this.nWords += deltaSize; - this.offset -= deltaSize; - this.data = rd; - } - } - return diff; - } - - - /** - * Multiplies by 10 a big integer represented as an array. The final carry - * is returned. - * - * @param src The array representation of the big integer. - * @param srcLen The number of elements of src to use. - * @param dst The product array. - * @return The final carry of the multiplication. - */ - /*@ - @ requires src.length >= srcLen && dst.length >= srcLen; - @ assignable dst[0 .. srcLen - 1]; - @ ensures 0 <= \result && \result < 10; - @ ensures AP(dst, srcLen) + (\result << (srcLen*32)) == \old(AP(src, srcLen) * 10); - @*/ - private static int multAndCarryBy10(int[] src, int srcLen, int[] dst) { - long carry = 0; - for (int i = 0; i < srcLen; i++) { - long product = (src[i] & LONG_MASK) * 10L + carry; - dst[i] = (int) product; - carry = product >>> 32; - } - return (int) carry; - } - - /** - * Multiplies by a constant value a big integer represented as an array. - * The constant factor is an int. - * - * @param src The array representation of the big integer. - * @param srcLen The number of elements of src to use. - * @param value The constant factor by which to multiply. - * @param dst The product array. - */ - /*@ - @ requires src.length >= srcLen && dst.length >= srcLen + 1; - @ assignable dst[0 .. srcLen]; - @ ensures AP(dst, srcLen + 1) == \old(AP(src, srcLen) * UNSIGNED(value)); - @*/ - private static void mult(int[] src, int srcLen, int value, int[] dst) { - long val = value & LONG_MASK; - long carry = 0; - for (int i = 0; i < srcLen; i++) { - long product = (src[i] & LONG_MASK) * val + carry; - dst[i] = (int) product; - carry = product >>> 32; - } - dst[srcLen] = (int) carry; - } - - /** - * Multiplies by a constant value a big integer represented as an array. - * The constant factor is a long represent as two ints. - * - * @param src The array representation of the big integer. - * @param srcLen The number of elements of src to use. - * @param v0 The lower 32 bits of the long factor. - * @param v1 The upper 32 bits of the long factor. - * @param dst The product array. - */ - /*@ - @ requires src != dst; - @ requires src.length >= srcLen && dst.length >= srcLen + 2; - @ assignable dst[0 .. srcLen + 1]; - @ ensures AP(dst, srcLen + 2) == \old(AP(src, srcLen) * (UNSIGNED(v0) + (UNSIGNED(v1) << 32))); - @*/ - private static void mult(int[] src, int srcLen, int v0, int v1, int[] dst) { - long v = v0 & LONG_MASK; - long carry = 0; - for (int j = 0; j < srcLen; j++) { - long product = v * (src[j] & LONG_MASK) + carry; - dst[j] = (int) product; - carry = product >>> 32; - } - dst[srcLen] = (int) carry; - v = v1 & LONG_MASK; - carry = 0; - for (int j = 0; j < srcLen; j++) { - long product = (dst[j + 1] & LONG_MASK) + v * (src[j] & LONG_MASK) + carry; - dst[j + 1] = (int) product; - carry = product >>> 32; - } - dst[srcLen + 1] = (int) carry; - } - - // Fails assertion for negative exponent. - /** - * Computes 5 raised to a given power. - * - * @param p The exponent of 5. - * @return 5p. - */ - private static FDBigInteger big5pow(int p) { - assert p >= 0 : p; // negative power of 5 - if (p < MAX_FIVE_POW) { - return POW_5_CACHE[p]; - } - return big5powRec(p); - } - - // slow path - /** - * Computes 5 raised to a given power. - * - * @param p The exponent of 5. - * @return 5p. - */ - private static FDBigInteger big5powRec(int p) { - if (p < MAX_FIVE_POW) { - return POW_5_CACHE[p]; - } - // construct the value. - // recursively. - int q, r; - // in order to compute 5^p, - // compute its square root, 5^(p/2) and square. - // or, let q = p / 2, r = p -q, then - // 5^p = 5^(q+r) = 5^q * 5^r - q = p >> 1; - r = p - q; - FDBigInteger bigq = big5powRec(q); - if (r < SMALL_5_POW.length) { - return bigq.mult(SMALL_5_POW[r]); - } else { - return bigq.mult(big5powRec(r)); - } - } - - // for debugging ... - /** - * Converts this FDBigInteger to a hexadecimal string. - * - * @return The hexadecimal string representation. - */ - public String toHexString(){ - if(nWords ==0) { - return "0"; - } - StringBuilder sb = new StringBuilder((nWords +offset)*8); - for(int i= nWords -1; i>=0; i--) { - String subStr = Integer.toHexString(data[i]); - for(int j = subStr.length(); j<8; j++) { - sb.append('0'); - } - sb.append(subStr); - } - for(int i=offset; i>0; i--) { - sb.append("00000000"); - } - return sb.toString(); - } - - // for debugging ... - /** - * Converts this FDBigInteger to a BigInteger. - * - * @return The BigInteger representation. - */ - public BigInteger toBigInteger() { - byte[] magnitude = new byte[nWords * 4 + 1]; - for (int i = 0; i < nWords; i++) { - int w = data[i]; - magnitude[magnitude.length - 4 * i - 1] = (byte) w; - magnitude[magnitude.length - 4 * i - 2] = (byte) (w >> 8); - magnitude[magnitude.length - 4 * i - 3] = (byte) (w >> 16); - magnitude[magnitude.length - 4 * i - 4] = (byte) (w >> 24); - } - return new BigInteger(magnitude).shiftLeft(offset * 32); - } - - // for debugging ... - /** - * Converts this FDBigInteger to a string. - * - * @return The string representation. - */ - @Override - public String toString(){ - return toBigInteger().toString(); - } -} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/misc/FloatConsts.java --- a/jdk/src/java.base/share/classes/sun/misc/FloatConsts.java Wed Dec 23 15:41:50 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.misc; - -/** - * This class contains additional constants documenting limits of the - * float type. - * - * @author Joseph D. Darcy - */ - -public class FloatConsts { - /** - * Don't let anyone instantiate this class. - */ - private FloatConsts() {} - - public static final float POSITIVE_INFINITY = java.lang.Float.POSITIVE_INFINITY; - public static final float NEGATIVE_INFINITY = java.lang.Float.NEGATIVE_INFINITY; - public static final float NaN = java.lang.Float.NaN; - public static final float MAX_VALUE = java.lang.Float.MAX_VALUE; - public static final float MIN_VALUE = java.lang.Float.MIN_VALUE; - - /** - * A constant holding the smallest positive normal value of type - * float, 2-126. It is equal to the value - * returned by Float.intBitsToFloat(0x00800000). - */ - public static final float MIN_NORMAL = 1.17549435E-38f; - - /** - * The number of logical bits in the significand of a - * float number, including the implicit bit. - */ - public static final int SIGNIFICAND_WIDTH = 24; - - /** - * Maximum exponent a finite float number may have. - * It is equal to the value returned by - * Math.ilogb(Float.MAX_VALUE). - */ - public static final int MAX_EXPONENT = 127; - - /** - * Minimum exponent a normalized float number may - * have. It is equal to the value returned by - * Math.ilogb(Float.MIN_NORMAL). - */ - public static final int MIN_EXPONENT = -126; - - /** - * The exponent the smallest positive float subnormal - * value would have if it could be normalized. - */ - public static final int MIN_SUB_EXPONENT = MIN_EXPONENT - - (SIGNIFICAND_WIDTH - 1); - - /** - * Bias used in representing a float exponent. - */ - public static final int EXP_BIAS = 127; - - /** - * Bit mask to isolate the sign bit of a float. - */ - public static final int SIGN_BIT_MASK = 0x80000000; - - /** - * Bit mask to isolate the exponent field of a - * float. - */ - public static final int EXP_BIT_MASK = 0x7F800000; - - /** - * Bit mask to isolate the significand field of a - * float. - */ - public static final int SIGNIF_BIT_MASK = 0x007FFFFF; - - static { - // verify bit masks cover all bit positions and that the bit - // masks are non-overlapping - assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0) && - (((SIGN_BIT_MASK & EXP_BIT_MASK) == 0) && - ((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0) && - ((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0))); - } -} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/misc/FloatingDecimal.java --- a/jdk/src/java.base/share/classes/sun/misc/FloatingDecimal.java Wed Dec 23 15:41:50 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2552 +0,0 @@ -/* - * Copyright (c) 1996, 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 sun.misc; - -import java.util.Arrays; -import java.util.regex.*; - -/** - * A class for converting between ASCII and decimal representations of a single - * or double precision floating point number. Most conversions are provided via - * static convenience methods, although a BinaryToASCIIConverter - * instance may be obtained and reused. - */ -public class FloatingDecimal{ - // - // Constants of the implementation; - // most are IEEE-754 related. - // (There are more really boring constants at the end.) - // - static final int EXP_SHIFT = DoubleConsts.SIGNIFICAND_WIDTH - 1; - static final long FRACT_HOB = ( 1L<String. - * - * @param d The double precision value. - * @return The value converted to a String. - */ - public static String toJavaFormatString(double d) { - return getBinaryToASCIIConverter(d).toJavaFormatString(); - } - - /** - * Converts a single precision floating point value to a String. - * - * @param f The single precision value. - * @return The value converted to a String. - */ - public static String toJavaFormatString(float f) { - return getBinaryToASCIIConverter(f).toJavaFormatString(); - } - - /** - * Appends a double precision floating point value to an Appendable. - * @param d The double precision value. - * @param buf The Appendable with the value appended. - */ - public static void appendTo(double d, Appendable buf) { - getBinaryToASCIIConverter(d).appendTo(buf); - } - - /** - * Appends a single precision floating point value to an Appendable. - * @param f The single precision value. - * @param buf The Appendable with the value appended. - */ - public static void appendTo(float f, Appendable buf) { - getBinaryToASCIIConverter(f).appendTo(buf); - } - - /** - * Converts a String to a double precision floating point value. - * - * @param s The String to convert. - * @return The double precision value. - * @throws NumberFormatException If the String does not - * represent a properly formatted double precision value. - */ - public static double parseDouble(String s) throws NumberFormatException { - return readJavaFormatString(s).doubleValue(); - } - - /** - * Converts a String to a single precision floating point value. - * - * @param s The String to convert. - * @return The single precision value. - * @throws NumberFormatException If the String does not - * represent a properly formatted single precision value. - */ - public static float parseFloat(String s) throws NumberFormatException { - return readJavaFormatString(s).floatValue(); - } - - /** - * A converter which can process single or double precision floating point - * values into an ASCII String representation. - */ - public interface BinaryToASCIIConverter { - /** - * Converts a floating point value into an ASCII String. - * @return The value converted to a String. - */ - public String toJavaFormatString(); - - /** - * Appends a floating point value to an Appendable. - * @param buf The Appendable to receive the value. - */ - public void appendTo(Appendable buf); - - /** - * Retrieves the decimal exponent most closely corresponding to this value. - * @return The decimal exponent. - */ - public int getDecimalExponent(); - - /** - * Retrieves the value as an array of digits. - * @param digits The digit array. - * @return The number of valid digits copied into the array. - */ - public int getDigits(char[] digits); - - /** - * Indicates the sign of the value. - * @return {@code value < 0.0}. - */ - public boolean isNegative(); - - /** - * Indicates whether the value is either infinite or not a number. - * - * @return true if and only if the value is NaN - * or infinite. - */ - public boolean isExceptional(); - - /** - * Indicates whether the value was rounded up during the binary to ASCII - * conversion. - * - * @return true if and only if the value was rounded up. - */ - public boolean digitsRoundedUp(); - - /** - * Indicates whether the binary to ASCII conversion was exact. - * - * @return true if any only if the conversion was exact. - */ - public boolean decimalDigitsExact(); - } - - /** - * A BinaryToASCIIConverter which represents NaN - * and infinite values. - */ - private static class ExceptionalBinaryToASCIIBuffer implements BinaryToASCIIConverter { - private final String image; - private boolean isNegative; - - public ExceptionalBinaryToASCIIBuffer(String image, boolean isNegative) { - this.image = image; - this.isNegative = isNegative; - } - - @Override - public String toJavaFormatString() { - return image; - } - - @Override - public void appendTo(Appendable buf) { - if (buf instanceof StringBuilder) { - ((StringBuilder) buf).append(image); - } else if (buf instanceof StringBuffer) { - ((StringBuffer) buf).append(image); - } else { - assert false; - } - } - - @Override - public int getDecimalExponent() { - throw new IllegalArgumentException("Exceptional value does not have an exponent"); - } - - @Override - public int getDigits(char[] digits) { - throw new IllegalArgumentException("Exceptional value does not have digits"); - } - - @Override - public boolean isNegative() { - return isNegative; - } - - @Override - public boolean isExceptional() { - return true; - } - - @Override - public boolean digitsRoundedUp() { - throw new IllegalArgumentException("Exceptional value is not rounded"); - } - - @Override - public boolean decimalDigitsExact() { - throw new IllegalArgumentException("Exceptional value is not exact"); - } - } - - private static final String INFINITY_REP = "Infinity"; - private static final int INFINITY_LENGTH = INFINITY_REP.length(); - private static final String NAN_REP = "NaN"; - private static final int NAN_LENGTH = NAN_REP.length(); - - private static final BinaryToASCIIConverter B2AC_POSITIVE_INFINITY = new ExceptionalBinaryToASCIIBuffer(INFINITY_REP, false); - private static final BinaryToASCIIConverter B2AC_NEGATIVE_INFINITY = new ExceptionalBinaryToASCIIBuffer("-" + INFINITY_REP, true); - private static final BinaryToASCIIConverter B2AC_NOT_A_NUMBER = new ExceptionalBinaryToASCIIBuffer(NAN_REP, false); - private static final BinaryToASCIIConverter B2AC_POSITIVE_ZERO = new BinaryToASCIIBuffer(false, new char[]{'0'}); - private static final BinaryToASCIIConverter B2AC_NEGATIVE_ZERO = new BinaryToASCIIBuffer(true, new char[]{'0'}); - - /** - * A buffered implementation of BinaryToASCIIConverter. - */ - static class BinaryToASCIIBuffer implements BinaryToASCIIConverter { - private boolean isNegative; - private int decExponent; - private int firstDigitIndex; - private int nDigits; - private final char[] digits; - private final char[] buffer = new char[26]; - - // - // The fields below provide additional information about the result of - // the binary to decimal digits conversion done in dtoa() and roundup() - // methods. They are changed if needed by those two methods. - // - - // True if the dtoa() binary to decimal conversion was exact. - private boolean exactDecimalConversion = false; - - // True if the result of the binary to decimal conversion was rounded-up - // at the end of the conversion process, i.e. roundUp() method was called. - private boolean decimalDigitsRoundedUp = false; - - /** - * Default constructor; used for non-zero values, - * BinaryToASCIIBuffer may be thread-local and reused - */ - BinaryToASCIIBuffer(){ - this.digits = new char[20]; - } - - /** - * Creates a specialized value (positive and negative zeros). - */ - BinaryToASCIIBuffer(boolean isNegative, char[] digits){ - this.isNegative = isNegative; - this.decExponent = 0; - this.digits = digits; - this.firstDigitIndex = 0; - this.nDigits = digits.length; - } - - @Override - public String toJavaFormatString() { - int len = getChars(buffer); - return new String(buffer, 0, len); - } - - @Override - public void appendTo(Appendable buf) { - int len = getChars(buffer); - if (buf instanceof StringBuilder) { - ((StringBuilder) buf).append(buffer, 0, len); - } else if (buf instanceof StringBuffer) { - ((StringBuffer) buf).append(buffer, 0, len); - } else { - assert false; - } - } - - @Override - public int getDecimalExponent() { - return decExponent; - } - - @Override - public int getDigits(char[] digits) { - System.arraycopy(this.digits,firstDigitIndex,digits,0,this.nDigits); - return this.nDigits; - } - - @Override - public boolean isNegative() { - return isNegative; - } - - @Override - public boolean isExceptional() { - return false; - } - - @Override - public boolean digitsRoundedUp() { - return decimalDigitsRoundedUp; - } - - @Override - public boolean decimalDigitsExact() { - return exactDecimalConversion; - } - - private void setSign(boolean isNegative) { - this.isNegative = isNegative; - } - - /** - * This is the easy subcase -- - * all the significant bits, after scaling, are held in lvalue. - * negSign and decExponent tell us what processing and scaling - * has already been done. Exceptional cases have already been - * stripped out. - * In particular: - * lvalue is a finite number (not Inf, nor NaN) - * lvalue > 0L (not zero, nor negative). - * - * The only reason that we develop the digits here, rather than - * calling on Long.toString() is that we can do it a little faster, - * and besides want to treat trailing 0s specially. If Long.toString - * changes, we should re-evaluate this strategy! - */ - private void developLongDigits( int decExponent, long lvalue, int insignificantDigits ){ - if ( insignificantDigits != 0 ){ - // Discard non-significant low-order bits, while rounding, - // up to insignificant value. - long pow10 = FDBigInteger.LONG_5_POW[insignificantDigits] << insignificantDigits; // 10^i == 5^i * 2^i; - long residue = lvalue % pow10; - lvalue /= pow10; - decExponent += insignificantDigits; - if ( residue >= (pow10>>1) ){ - // round up based on the low-order bits we're discarding - lvalue++; - } - } - int digitno = digits.length -1; - int c; - if ( lvalue <= Integer.MAX_VALUE ){ - assert lvalue > 0L : lvalue; // lvalue <= 0 - // even easier subcase! - // can do int arithmetic rather than long! - int ivalue = (int)lvalue; - c = ivalue%10; - ivalue /= 10; - while ( c == 0 ){ - decExponent++; - c = ivalue%10; - ivalue /= 10; - } - while ( ivalue != 0){ - digits[digitno--] = (char)(c+'0'); - decExponent++; - c = ivalue%10; - ivalue /= 10; - } - digits[digitno] = (char)(c+'0'); - } else { - // same algorithm as above (same bugs, too ) - // but using long arithmetic. - c = (int)(lvalue%10L); - lvalue /= 10L; - while ( c == 0 ){ - decExponent++; - c = (int)(lvalue%10L); - lvalue /= 10L; - } - while ( lvalue != 0L ){ - digits[digitno--] = (char)(c+'0'); - decExponent++; - c = (int)(lvalue%10L); - lvalue /= 10; - } - digits[digitno] = (char)(c+'0'); - } - this.decExponent = decExponent+1; - this.firstDigitIndex = digitno; - this.nDigits = this.digits.length - digitno; - } - - private void dtoa( int binExp, long fractBits, int nSignificantBits, boolean isCompatibleFormat) - { - assert fractBits > 0 ; // fractBits here can't be zero or negative - assert (fractBits & FRACT_HOB)!=0 ; // Hi-order bit should be set - // Examine number. Determine if it is an easy case, - // which we can do pretty trivially using float/long conversion, - // or whether we must do real work. - final int tailZeros = Long.numberOfTrailingZeros(fractBits); - - // number of significant bits of fractBits; - final int nFractBits = EXP_SHIFT+1-tailZeros; - - // reset flags to default values as dtoa() does not always set these - // flags and a prior call to dtoa() might have set them to incorrect - // values with respect to the current state. - decimalDigitsRoundedUp = false; - exactDecimalConversion = false; - - // number of significant bits to the right of the point. - int nTinyBits = Math.max( 0, nFractBits - binExp - 1 ); - if ( binExp <= MAX_SMALL_BIN_EXP && binExp >= MIN_SMALL_BIN_EXP ){ - // Look more closely at the number to decide if, - // with scaling by 10^nTinyBits, the result will fit in - // a long. - if ( (nTinyBits < FDBigInteger.LONG_5_POW.length) && ((nFractBits + N_5_BITS[nTinyBits]) < 64 ) ){ - // - // We can do this: - // take the fraction bits, which are normalized. - // (a) nTinyBits == 0: Shift left or right appropriately - // to align the binary point at the extreme right, i.e. - // where a long int point is expected to be. The integer - // result is easily converted to a string. - // (b) nTinyBits > 0: Shift right by EXP_SHIFT-nFractBits, - // which effectively converts to long and scales by - // 2^nTinyBits. Then multiply by 5^nTinyBits to - // complete the scaling. We know this won't overflow - // because we just counted the number of bits necessary - // in the result. The integer you get from this can - // then be converted to a string pretty easily. - // - if ( nTinyBits == 0 ) { - int insignificant; - if ( binExp > nSignificantBits ){ - insignificant = insignificantDigitsForPow2(binExp-nSignificantBits-1); - } else { - insignificant = 0; - } - if ( binExp >= EXP_SHIFT ){ - fractBits <<= (binExp-EXP_SHIFT); - } else { - fractBits >>>= (EXP_SHIFT-binExp) ; - } - developLongDigits( 0, fractBits, insignificant ); - return; - } - // - // The following causes excess digits to be printed - // out in the single-float case. Our manipulation of - // halfULP here is apparently not correct. If we - // better understand how this works, perhaps we can - // use this special case again. But for the time being, - // we do not. - // else { - // fractBits >>>= EXP_SHIFT+1-nFractBits; - // fractBits//= long5pow[ nTinyBits ]; - // halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits); - // developLongDigits( -nTinyBits, fractBits, insignificantDigits(halfULP) ); - // return; - // } - // - } - } - // - // This is the hard case. We are going to compute large positive - // integers B and S and integer decExp, s.t. - // d = ( B / S )// 10^decExp - // 1 <= B / S < 10 - // Obvious choices are: - // decExp = floor( log10(d) ) - // B = d// 2^nTinyBits// 10^max( 0, -decExp ) - // S = 10^max( 0, decExp)// 2^nTinyBits - // (noting that nTinyBits has already been forced to non-negative) - // I am also going to compute a large positive integer - // M = (1/2^nSignificantBits)// 2^nTinyBits// 10^max( 0, -decExp ) - // i.e. M is (1/2) of the ULP of d, scaled like B. - // When we iterate through dividing B/S and picking off the - // quotient bits, we will know when to stop when the remainder - // is <= M. - // - // We keep track of powers of 2 and powers of 5. - // - int decExp = estimateDecExp(fractBits,binExp); - int B2, B5; // powers of 2 and powers of 5, respectively, in B - int S2, S5; // powers of 2 and powers of 5, respectively, in S - int M2, M5; // powers of 2 and powers of 5, respectively, in M - - B5 = Math.max( 0, -decExp ); - B2 = B5 + nTinyBits + binExp; - - S5 = Math.max( 0, decExp ); - S2 = S5 + nTinyBits; - - M5 = B5; - M2 = B2 - nSignificantBits; - - // - // the long integer fractBits contains the (nFractBits) interesting - // bits from the mantissa of d ( hidden 1 added if necessary) followed - // by (EXP_SHIFT+1-nFractBits) zeros. In the interest of compactness, - // I will shift out those zeros before turning fractBits into a - // FDBigInteger. The resulting whole number will be - // d * 2^(nFractBits-1-binExp). - // - fractBits >>>= tailZeros; - B2 -= nFractBits-1; - int common2factor = Math.min( B2, S2 ); - B2 -= common2factor; - S2 -= common2factor; - M2 -= common2factor; - - // - // HACK!! For exact powers of two, the next smallest number - // is only half as far away as we think (because the meaning of - // ULP changes at power-of-two bounds) for this reason, we - // hack M2. Hope this works. - // - if ( nFractBits == 1 ) { - M2 -= 1; - } - - if ( M2 < 0 ){ - // oops. - // since we cannot scale M down far enough, - // we must scale the other values up. - B2 -= M2; - S2 -= M2; - M2 = 0; - } - // - // Construct, Scale, iterate. - // Some day, we'll write a stopping test that takes - // account of the asymmetry of the spacing of floating-point - // numbers below perfect powers of 2 - // 26 Sept 96 is not that day. - // So we use a symmetric test. - // - int ndigit = 0; - boolean low, high; - long lowDigitDifference; - int q; - - // - // Detect the special cases where all the numbers we are about - // to compute will fit in int or long integers. - // In these cases, we will avoid doing FDBigInteger arithmetic. - // We use the same algorithms, except that we "normalize" - // our FDBigIntegers before iterating. This is to make division easier, - // as it makes our fist guess (quotient of high-order words) - // more accurate! - // - // Some day, we'll write a stopping test that takes - // account of the asymmetry of the spacing of floating-point - // numbers below perfect powers of 2 - // 26 Sept 96 is not that day. - // So we use a symmetric test. - // - // binary digits needed to represent B, approx. - int Bbits = nFractBits + B2 + (( B5 < N_5_BITS.length )? N_5_BITS[B5] : ( B5*3 )); - - // binary digits needed to represent 10*S, approx. - int tenSbits = S2+1 + (( (S5+1) < N_5_BITS.length )? N_5_BITS[(S5+1)] : ( (S5+1)*3 )); - if ( Bbits < 64 && tenSbits < 64){ - if ( Bbits < 32 && tenSbits < 32){ - // wa-hoo! They're all ints! - int b = ((int)fractBits * FDBigInteger.SMALL_5_POW[B5] ) << B2; - int s = FDBigInteger.SMALL_5_POW[S5] << S2; - int m = FDBigInteger.SMALL_5_POW[M5] << M2; - int tens = s * 10; - // - // Unroll the first iteration. If our decExp estimate - // was too high, our first quotient will be zero. In this - // case, we discard it and decrement decExp. - // - ndigit = 0; - q = b / s; - b = 10 * ( b % s ); - m *= 10; - low = (b < m ); - high = (b+m > tens ); - assert q < 10 : q; // excessively large digit - if ( (q == 0) && ! high ){ - // oops. Usually ignore leading zero. - decExp--; - } else { - digits[ndigit++] = (char)('0' + q); - } - // - // HACK! Java spec sez that we always have at least - // one digit after the . in either F- or E-form output. - // Thus we will need more than one digit if we're using - // E-form - // - if ( !isCompatibleFormat ||decExp < -3 || decExp >= 8 ){ - high = low = false; - } - while( ! low && ! high ){ - q = b / s; - b = 10 * ( b % s ); - m *= 10; - assert q < 10 : q; // excessively large digit - if ( m > 0L ){ - low = (b < m ); - high = (b+m > tens ); - } else { - // hack -- m might overflow! - // in this case, it is certainly > b, - // which won't - // and b+m > tens, too, since that has overflowed - // either! - low = true; - high = true; - } - digits[ndigit++] = (char)('0' + q); - } - lowDigitDifference = (b<<1) - tens; - exactDecimalConversion = (b == 0); - } else { - // still good! they're all longs! - long b = (fractBits * FDBigInteger.LONG_5_POW[B5] ) << B2; - long s = FDBigInteger.LONG_5_POW[S5] << S2; - long m = FDBigInteger.LONG_5_POW[M5] << M2; - long tens = s * 10L; - // - // Unroll the first iteration. If our decExp estimate - // was too high, our first quotient will be zero. In this - // case, we discard it and decrement decExp. - // - ndigit = 0; - q = (int) ( b / s ); - b = 10L * ( b % s ); - m *= 10L; - low = (b < m ); - high = (b+m > tens ); - assert q < 10 : q; // excessively large digit - if ( (q == 0) && ! high ){ - // oops. Usually ignore leading zero. - decExp--; - } else { - digits[ndigit++] = (char)('0' + q); - } - // - // HACK! Java spec sez that we always have at least - // one digit after the . in either F- or E-form output. - // Thus we will need more than one digit if we're using - // E-form - // - if ( !isCompatibleFormat || decExp < -3 || decExp >= 8 ){ - high = low = false; - } - while( ! low && ! high ){ - q = (int) ( b / s ); - b = 10 * ( b % s ); - m *= 10; - assert q < 10 : q; // excessively large digit - if ( m > 0L ){ - low = (b < m ); - high = (b+m > tens ); - } else { - // hack -- m might overflow! - // in this case, it is certainly > b, - // which won't - // and b+m > tens, too, since that has overflowed - // either! - low = true; - high = true; - } - digits[ndigit++] = (char)('0' + q); - } - lowDigitDifference = (b<<1) - tens; - exactDecimalConversion = (b == 0); - } - } else { - // - // We really must do FDBigInteger arithmetic. - // Fist, construct our FDBigInteger initial values. - // - FDBigInteger Sval = FDBigInteger.valueOfPow52(S5, S2); - int shiftBias = Sval.getNormalizationBias(); - Sval = Sval.leftShift(shiftBias); // normalize so that division works better - - FDBigInteger Bval = FDBigInteger.valueOfMulPow52(fractBits, B5, B2 + shiftBias); - FDBigInteger Mval = FDBigInteger.valueOfPow52(M5 + 1, M2 + shiftBias + 1); - - FDBigInteger tenSval = FDBigInteger.valueOfPow52(S5 + 1, S2 + shiftBias + 1); //Sval.mult( 10 ); - // - // Unroll the first iteration. If our decExp estimate - // was too high, our first quotient will be zero. In this - // case, we discard it and decrement decExp. - // - ndigit = 0; - q = Bval.quoRemIteration( Sval ); - low = (Bval.cmp( Mval ) < 0); - high = tenSval.addAndCmp(Bval,Mval)<=0; - - assert q < 10 : q; // excessively large digit - if ( (q == 0) && ! high ){ - // oops. Usually ignore leading zero. - decExp--; - } else { - digits[ndigit++] = (char)('0' + q); - } - // - // HACK! Java spec sez that we always have at least - // one digit after the . in either F- or E-form output. - // Thus we will need more than one digit if we're using - // E-form - // - if (!isCompatibleFormat || decExp < -3 || decExp >= 8 ){ - high = low = false; - } - while( ! low && ! high ){ - q = Bval.quoRemIteration( Sval ); - assert q < 10 : q; // excessively large digit - Mval = Mval.multBy10(); //Mval = Mval.mult( 10 ); - low = (Bval.cmp( Mval ) < 0); - high = tenSval.addAndCmp(Bval,Mval)<=0; - digits[ndigit++] = (char)('0' + q); - } - if ( high && low ){ - Bval = Bval.leftShift(1); - lowDigitDifference = Bval.cmp(tenSval); - } else { - lowDigitDifference = 0L; // this here only for flow analysis! - } - exactDecimalConversion = (Bval.cmp( FDBigInteger.ZERO ) == 0); - } - this.decExponent = decExp+1; - this.firstDigitIndex = 0; - this.nDigits = ndigit; - // - // Last digit gets rounded based on stopping condition. - // - if ( high ){ - if ( low ){ - if ( lowDigitDifference == 0L ){ - // it's a tie! - // choose based on which digits we like. - if ( (digits[firstDigitIndex+nDigits-1]&1) != 0 ) { - roundup(); - } - } else if ( lowDigitDifference > 0 ){ - roundup(); - } - } else { - roundup(); - } - } - } - - // add one to the least significant digit. - // in the unlikely event there is a carry out, deal with it. - // assert that this will only happen where there - // is only one digit, e.g. (float)1e-44 seems to do it. - // - private void roundup() { - int i = (firstDigitIndex + nDigits - 1); - int q = digits[i]; - if (q == '9') { - while (q == '9' && i > firstDigitIndex) { - digits[i] = '0'; - q = digits[--i]; - } - if (q == '9') { - // carryout! High-order 1, rest 0s, larger exp. - decExponent += 1; - digits[firstDigitIndex] = '1'; - return; - } - // else fall through. - } - digits[i] = (char) (q + 1); - decimalDigitsRoundedUp = true; - } - - /** - * Estimate decimal exponent. (If it is small-ish, - * we could double-check.) - * - * First, scale the mantissa bits such that 1 <= d2 < 2. - * We are then going to estimate - * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5) - * and so we can estimate - * log10(d) ~=~ log10(d2) + binExp * log10(2) - * take the floor and call it decExp. - */ - static int estimateDecExp(long fractBits, int binExp) { - double d2 = Double.longBitsToDouble( EXP_ONE | ( fractBits & DoubleConsts.SIGNIF_BIT_MASK ) ); - double d = (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981; - long dBits = Double.doubleToRawLongBits(d); //can't be NaN here so use raw - int exponent = (int)((dBits & DoubleConsts.EXP_BIT_MASK) >> EXP_SHIFT) - DoubleConsts.EXP_BIAS; - boolean isNegative = (dBits & DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign - if(exponent>=0 && exponent<52) { // hot path - long mask = DoubleConsts.SIGNIF_BIT_MASK >> exponent; - int r = (int)(( (dBits&DoubleConsts.SIGNIF_BIT_MASK) | FRACT_HOB )>>(EXP_SHIFT-exponent)); - return isNegative ? (((mask & dBits) == 0L ) ? -r : -r-1 ) : r; - } else if (exponent < 0) { - return (((dBits&~DoubleConsts.SIGN_BIT_MASK) == 0) ? 0 : - ( (isNegative) ? -1 : 0) ); - } else { //if (exponent >= 52) - return (int)d; - } - } - - private static int insignificantDigits(int insignificant) { - int i; - for ( i = 0; insignificant >= 10L; i++ ) { - insignificant /= 10L; - } - return i; - } - - /** - * Calculates - *
    -         * insignificantDigitsForPow2(v) == insignificantDigits(1L<
    -         */
    -        private static int insignificantDigitsForPow2(int p2) {
    -            if(p2>1 && p2 < insignificantDigitsNumber.length) {
    -                return insignificantDigitsNumber[p2];
    -            }
    -            return 0;
    -        }
    -
    -        /**
    -         *  If insignificant==(1L << ixd)
    -         *  i = insignificantDigitsNumber[idx] is the same as:
    -         *  int i;
    -         *  for ( i = 0; insignificant >= 10L; i++ )
    -         *         insignificant /= 10L;
    -         */
    -        private static int[] insignificantDigitsNumber = {
    -            0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3,
    -            4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
    -            8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11,
    -            12, 12, 12, 12, 13, 13, 13, 14, 14, 14,
    -            15, 15, 15, 15, 16, 16, 16, 17, 17, 17,
    -            18, 18, 18, 19
    -        };
    -
    -        // approximately ceil( log2( long5pow[i] ) )
    -        private static final int[] N_5_BITS = {
    -                0,
    -                3,
    -                5,
    -                7,
    -                10,
    -                12,
    -                14,
    -                17,
    -                19,
    -                21,
    -                24,
    -                26,
    -                28,
    -                31,
    -                33,
    -                35,
    -                38,
    -                40,
    -                42,
    -                45,
    -                47,
    -                49,
    -                52,
    -                54,
    -                56,
    -                59,
    -                61,
    -        };
    -
    -        private int getChars(char[] result) {
    -            assert nDigits <= 19 : nDigits; // generous bound on size of nDigits
    -            int i = 0;
    -            if (isNegative) {
    -                result[0] = '-';
    -                i = 1;
    -            }
    -            if (decExponent > 0 && decExponent < 8) {
    -                // print digits.digits.
    -                int charLength = Math.min(nDigits, decExponent);
    -                System.arraycopy(digits, firstDigitIndex, result, i, charLength);
    -                i += charLength;
    -                if (charLength < decExponent) {
    -                    charLength = decExponent - charLength;
    -                    Arrays.fill(result,i,i+charLength,'0');
    -                    i += charLength;
    -                    result[i++] = '.';
    -                    result[i++] = '0';
    -                } else {
    -                    result[i++] = '.';
    -                    if (charLength < nDigits) {
    -                        int t = nDigits - charLength;
    -                        System.arraycopy(digits, firstDigitIndex+charLength, result, i, t);
    -                        i += t;
    -                    } else {
    -                        result[i++] = '0';
    -                    }
    -                }
    -            } else if (decExponent <= 0 && decExponent > -3) {
    -                result[i++] = '0';
    -                result[i++] = '.';
    -                if (decExponent != 0) {
    -                    Arrays.fill(result, i, i-decExponent, '0');
    -                    i -= decExponent;
    -                }
    -                System.arraycopy(digits, firstDigitIndex, result, i, nDigits);
    -                i += nDigits;
    -            } else {
    -                result[i++] = digits[firstDigitIndex];
    -                result[i++] = '.';
    -                if (nDigits > 1) {
    -                    System.arraycopy(digits, firstDigitIndex+1, result, i, nDigits - 1);
    -                    i += nDigits - 1;
    -                } else {
    -                    result[i++] = '0';
    -                }
    -                result[i++] = 'E';
    -                int e;
    -                if (decExponent <= 0) {
    -                    result[i++] = '-';
    -                    e = -decExponent + 1;
    -                } else {
    -                    e = decExponent - 1;
    -                }
    -                // decExponent has 1, 2, or 3, digits
    -                if (e <= 9) {
    -                    result[i++] = (char) (e + '0');
    -                } else if (e <= 99) {
    -                    result[i++] = (char) (e / 10 + '0');
    -                    result[i++] = (char) (e % 10 + '0');
    -                } else {
    -                    result[i++] = (char) (e / 100 + '0');
    -                    e %= 100;
    -                    result[i++] = (char) (e / 10 + '0');
    -                    result[i++] = (char) (e % 10 + '0');
    -                }
    -            }
    -            return i;
    -        }
    -
    -    }
    -
    -    private static final ThreadLocal threadLocalBinaryToASCIIBuffer =
    -            new ThreadLocal() {
    -                @Override
    -                protected BinaryToASCIIBuffer initialValue() {
    -                    return new BinaryToASCIIBuffer();
    -                }
    -            };
    -
    -    private static BinaryToASCIIBuffer getBinaryToASCIIBuffer() {
    -        return threadLocalBinaryToASCIIBuffer.get();
    -    }
    -
    -    /**
    -     * A converter which can process an ASCII String representation
    -     * of a single or double precision floating point value into a
    -     * float or a double.
    -     */
    -    interface ASCIIToBinaryConverter {
    -
    -        double doubleValue();
    -
    -        float floatValue();
    -
    -    }
    -
    -    /**
    -     * A ASCIIToBinaryConverter container for a double.
    -     */
    -    static class PreparedASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
    -        private final double doubleVal;
    -        private final float floatVal;
    -
    -        public PreparedASCIIToBinaryBuffer(double doubleVal, float floatVal) {
    -            this.doubleVal = doubleVal;
    -            this.floatVal = floatVal;
    -        }
    -
    -        @Override
    -        public double doubleValue() {
    -            return doubleVal;
    -        }
    -
    -        @Override
    -        public float floatValue() {
    -            return floatVal;
    -        }
    -    }
    -
    -    static final ASCIIToBinaryConverter A2BC_POSITIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
    -    static final ASCIIToBinaryConverter A2BC_NEGATIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
    -    static final ASCIIToBinaryConverter A2BC_NOT_A_NUMBER  = new PreparedASCIIToBinaryBuffer(Double.NaN, Float.NaN);
    -    static final ASCIIToBinaryConverter A2BC_POSITIVE_ZERO = new PreparedASCIIToBinaryBuffer(0.0d, 0.0f);
    -    static final ASCIIToBinaryConverter A2BC_NEGATIVE_ZERO = new PreparedASCIIToBinaryBuffer(-0.0d, -0.0f);
    -
    -    /**
    -     * A buffered implementation of ASCIIToBinaryConverter.
    -     */
    -    static class ASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
    -        boolean     isNegative;
    -        int         decExponent;
    -        char        digits[];
    -        int         nDigits;
    -
    -        ASCIIToBinaryBuffer( boolean negSign, int decExponent, char[] digits, int n)
    -        {
    -            this.isNegative = negSign;
    -            this.decExponent = decExponent;
    -            this.digits = digits;
    -            this.nDigits = n;
    -        }
    -
    -        /**
    -         * Takes a FloatingDecimal, which we presumably just scanned in,
    -         * and finds out what its value is, as a double.
    -         *
    -         * AS A SIDE EFFECT, SET roundDir TO INDICATE PREFERRED
    -         * ROUNDING DIRECTION in case the result is really destined
    -         * for a single-precision float.
    -         */
    -        @Override
    -        public double doubleValue() {
    -            int kDigits = Math.min(nDigits, MAX_DECIMAL_DIGITS + 1);
    -            //
    -            // convert the lead kDigits to a long integer.
    -            //
    -            // (special performance hack: start to do it using int)
    -            int iValue = (int) digits[0] - (int) '0';
    -            int iDigits = Math.min(kDigits, INT_DECIMAL_DIGITS);
    -            for (int i = 1; i < iDigits; i++) {
    -                iValue = iValue * 10 + (int) digits[i] - (int) '0';
    -            }
    -            long lValue = (long) iValue;
    -            for (int i = iDigits; i < kDigits; i++) {
    -                lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
    -            }
    -            double dValue = (double) lValue;
    -            int exp = decExponent - kDigits;
    -            //
    -            // lValue now contains a long integer with the value of
    -            // the first kDigits digits of the number.
    -            // dValue contains the (double) of the same.
    -            //
    -
    -            if (nDigits <= MAX_DECIMAL_DIGITS) {
    -                //
    -                // possibly an easy case.
    -                // We know that the digits can be represented
    -                // exactly. And if the exponent isn't too outrageous,
    -                // the whole thing can be done with one operation,
    -                // thus one rounding error.
    -                // Note that all our constructors trim all leading and
    -                // trailing zeros, so simple values (including zero)
    -                // will always end up here
    -                //
    -                if (exp == 0 || dValue == 0.0) {
    -                    return (isNegative) ? -dValue : dValue; // small floating integer
    -                }
    -                else if (exp >= 0) {
    -                    if (exp <= MAX_SMALL_TEN) {
    -                        //
    -                        // Can get the answer with one operation,
    -                        // thus one roundoff.
    -                        //
    -                        double rValue = dValue * SMALL_10_POW[exp];
    -                        return (isNegative) ? -rValue : rValue;
    -                    }
    -                    int slop = MAX_DECIMAL_DIGITS - kDigits;
    -                    if (exp <= MAX_SMALL_TEN + slop) {
    -                        //
    -                        // We can multiply dValue by 10^(slop)
    -                        // and it is still "small" and exact.
    -                        // Then we can multiply by 10^(exp-slop)
    -                        // with one rounding.
    -                        //
    -                        dValue *= SMALL_10_POW[slop];
    -                        double rValue = dValue * SMALL_10_POW[exp - slop];
    -                        return (isNegative) ? -rValue : rValue;
    -                    }
    -                    //
    -                    // Else we have a hard case with a positive exp.
    -                    //
    -                } else {
    -                    if (exp >= -MAX_SMALL_TEN) {
    -                        //
    -                        // Can get the answer in one division.
    -                        //
    -                        double rValue = dValue / SMALL_10_POW[-exp];
    -                        return (isNegative) ? -rValue : rValue;
    -                    }
    -                    //
    -                    // Else we have a hard case with a negative exp.
    -                    //
    -                }
    -            }
    -
    -            //
    -            // Harder cases:
    -            // The sum of digits plus exponent is greater than
    -            // what we think we can do with one error.
    -            //
    -            // Start by approximating the right answer by,
    -            // naively, scaling by powers of 10.
    -            //
    -            if (exp > 0) {
    -                if (decExponent > MAX_DECIMAL_EXPONENT + 1) {
    -                    //
    -                    // Lets face it. This is going to be
    -                    // Infinity. Cut to the chase.
    -                    //
    -                    return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
    -                }
    -                if ((exp & 15) != 0) {
    -                    dValue *= SMALL_10_POW[exp & 15];
    -                }
    -                if ((exp >>= 4) != 0) {
    -                    int j;
    -                    for (j = 0; exp > 1; j++, exp >>= 1) {
    -                        if ((exp & 1) != 0) {
    -                            dValue *= BIG_10_POW[j];
    -                        }
    -                    }
    -                    //
    -                    // The reason for the weird exp > 1 condition
    -                    // in the above loop was so that the last multiply
    -                    // would get unrolled. We handle it here.
    -                    // It could overflow.
    -                    //
    -                    double t = dValue * BIG_10_POW[j];
    -                    if (Double.isInfinite(t)) {
    -                        //
    -                        // It did overflow.
    -                        // Look more closely at the result.
    -                        // If the exponent is just one too large,
    -                        // then use the maximum finite as our estimate
    -                        // value. Else call the result infinity
    -                        // and punt it.
    -                        // ( I presume this could happen because
    -                        // rounding forces the result here to be
    -                        // an ULP or two larger than
    -                        // Double.MAX_VALUE ).
    -                        //
    -                        t = dValue / 2.0;
    -                        t *= BIG_10_POW[j];
    -                        if (Double.isInfinite(t)) {
    -                            return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
    -                        }
    -                        t = Double.MAX_VALUE;
    -                    }
    -                    dValue = t;
    -                }
    -            } else if (exp < 0) {
    -                exp = -exp;
    -                if (decExponent < MIN_DECIMAL_EXPONENT - 1) {
    -                    //
    -                    // Lets face it. This is going to be
    -                    // zero. Cut to the chase.
    -                    //
    -                    return (isNegative) ? -0.0 : 0.0;
    -                }
    -                if ((exp & 15) != 0) {
    -                    dValue /= SMALL_10_POW[exp & 15];
    -                }
    -                if ((exp >>= 4) != 0) {
    -                    int j;
    -                    for (j = 0; exp > 1; j++, exp >>= 1) {
    -                        if ((exp & 1) != 0) {
    -                            dValue *= TINY_10_POW[j];
    -                        }
    -                    }
    -                    //
    -                    // The reason for the weird exp > 1 condition
    -                    // in the above loop was so that the last multiply
    -                    // would get unrolled. We handle it here.
    -                    // It could underflow.
    -                    //
    -                    double t = dValue * TINY_10_POW[j];
    -                    if (t == 0.0) {
    -                        //
    -                        // It did underflow.
    -                        // Look more closely at the result.
    -                        // If the exponent is just one too small,
    -                        // then use the minimum finite as our estimate
    -                        // value. Else call the result 0.0
    -                        // and punt it.
    -                        // ( I presume this could happen because
    -                        // rounding forces the result here to be
    -                        // an ULP or two less than
    -                        // Double.MIN_VALUE ).
    -                        //
    -                        t = dValue * 2.0;
    -                        t *= TINY_10_POW[j];
    -                        if (t == 0.0) {
    -                            return (isNegative) ? -0.0 : 0.0;
    -                        }
    -                        t = Double.MIN_VALUE;
    -                    }
    -                    dValue = t;
    -                }
    -            }
    -
    -            //
    -            // dValue is now approximately the result.
    -            // The hard part is adjusting it, by comparison
    -            // with FDBigInteger arithmetic.
    -            // Formulate the EXACT big-number result as
    -            // bigD0 * 10^exp
    -            //
    -            if (nDigits > MAX_NDIGITS) {
    -                nDigits = MAX_NDIGITS + 1;
    -                digits[MAX_NDIGITS] = '1';
    -            }
    -            FDBigInteger bigD0 = new FDBigInteger(lValue, digits, kDigits, nDigits);
    -            exp = decExponent - nDigits;
    -
    -            long ieeeBits = Double.doubleToRawLongBits(dValue); // IEEE-754 bits of double candidate
    -            final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
    -            final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
    -            bigD0 = bigD0.multByPow52(D5, 0);
    -            bigD0.makeImmutable();   // prevent bigD0 modification inside correctionLoop
    -            FDBigInteger bigD = null;
    -            int prevD2 = 0;
    -
    -            correctionLoop:
    -            while (true) {
    -                // here ieeeBits can't be NaN, Infinity or zero
    -                int binexp = (int) (ieeeBits >>> EXP_SHIFT);
    -                long bigBbits = ieeeBits & DoubleConsts.SIGNIF_BIT_MASK;
    -                if (binexp > 0) {
    -                    bigBbits |= FRACT_HOB;
    -                } else { // Normalize denormalized numbers.
    -                    assert bigBbits != 0L : bigBbits; // doubleToBigInt(0.0)
    -                    int leadingZeros = Long.numberOfLeadingZeros(bigBbits);
    -                    int shift = leadingZeros - (63 - EXP_SHIFT);
    -                    bigBbits <<= shift;
    -                    binexp = 1 - shift;
    -                }
    -                binexp -= DoubleConsts.EXP_BIAS;
    -                int lowOrderZeros = Long.numberOfTrailingZeros(bigBbits);
    -                bigBbits >>>= lowOrderZeros;
    -                final int bigIntExp = binexp - EXP_SHIFT + lowOrderZeros;
    -                final int bigIntNBits = EXP_SHIFT + 1 - lowOrderZeros;
    -
    -                //
    -                // Scale bigD, bigB appropriately for
    -                // big-integer operations.
    -                // Naively, we multiply by powers of ten
    -                // and powers of two. What we actually do
    -                // is keep track of the powers of 5 and
    -                // powers of 2 we would use, then factor out
    -                // common divisors before doing the work.
    -                //
    -                int B2 = B5; // powers of 2 in bigB
    -                int D2 = D5; // powers of 2 in bigD
    -                int Ulp2;   // powers of 2 in halfUlp.
    -                if (bigIntExp >= 0) {
    -                    B2 += bigIntExp;
    -                } else {
    -                    D2 -= bigIntExp;
    -                }
    -                Ulp2 = B2;
    -                // shift bigB and bigD left by a number s. t.
    -                // halfUlp is still an integer.
    -                int hulpbias;
    -                if (binexp <= -DoubleConsts.EXP_BIAS) {
    -                    // This is going to be a denormalized number
    -                    // (if not actually zero).
    -                    // half an ULP is at 2^-(DoubleConsts.EXP_BIAS+EXP_SHIFT+1)
    -                    hulpbias = binexp + lowOrderZeros + DoubleConsts.EXP_BIAS;
    -                } else {
    -                    hulpbias = 1 + lowOrderZeros;
    -                }
    -                B2 += hulpbias;
    -                D2 += hulpbias;
    -                // if there are common factors of 2, we might just as well
    -                // factor them out, as they add nothing useful.
    -                int common2 = Math.min(B2, Math.min(D2, Ulp2));
    -                B2 -= common2;
    -                D2 -= common2;
    -                Ulp2 -= common2;
    -                // do multiplications by powers of 5 and 2
    -                FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
    -                if (bigD == null || prevD2 != D2) {
    -                    bigD = bigD0.leftShift(D2);
    -                    prevD2 = D2;
    -                }
    -                //
    -                // to recap:
    -                // bigB is the scaled-big-int version of our floating-point
    -                // candidate.
    -                // bigD is the scaled-big-int version of the exact value
    -                // as we understand it.
    -                // halfUlp is 1/2 an ulp of bigB, except for special cases
    -                // of exact powers of 2
    -                //
    -                // the plan is to compare bigB with bigD, and if the difference
    -                // is less than halfUlp, then we're satisfied. Otherwise,
    -                // use the ratio of difference to halfUlp to calculate a fudge
    -                // factor to add to the floating value, then go 'round again.
    -                //
    -                FDBigInteger diff;
    -                int cmpResult;
    -                boolean overvalue;
    -                if ((cmpResult = bigB.cmp(bigD)) > 0) {
    -                    overvalue = true; // our candidate is too big.
    -                    diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
    -                    if ((bigIntNBits == 1) && (bigIntExp > -DoubleConsts.EXP_BIAS + 1)) {
    -                        // candidate is a normalized exact power of 2 and
    -                        // is too big (larger than Double.MIN_NORMAL). We will be subtracting.
    -                        // For our purposes, ulp is the ulp of the
    -                        // next smaller range.
    -                        Ulp2 -= 1;
    -                        if (Ulp2 < 0) {
    -                            // rats. Cannot de-scale ulp this far.
    -                            // must scale diff in other direction.
    -                            Ulp2 = 0;
    -                            diff = diff.leftShift(1);
    -                        }
    -                    }
    -                } else if (cmpResult < 0) {
    -                    overvalue = false; // our candidate is too small.
    -                    diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
    -                } else {
    -                    // the candidate is exactly right!
    -                    // this happens with surprising frequency
    -                    break correctionLoop;
    -                }
    -                cmpResult = diff.cmpPow52(B5, Ulp2);
    -                if ((cmpResult) < 0) {
    -                    // difference is small.
    -                    // this is close enough
    -                    break correctionLoop;
    -                } else if (cmpResult == 0) {
    -                    // difference is exactly half an ULP
    -                    // round to some other value maybe, then finish
    -                    if ((ieeeBits & 1) != 0) { // half ties to even
    -                        ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    -                    }
    -                    break correctionLoop;
    -                } else {
    -                    // difference is non-trivial.
    -                    // could scale addend by ratio of difference to
    -                    // halfUlp here, if we bothered to compute that difference.
    -                    // Most of the time ( I hope ) it is about 1 anyway.
    -                    ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    -                    if (ieeeBits == 0 || ieeeBits == DoubleConsts.EXP_BIT_MASK) { // 0.0 or Double.POSITIVE_INFINITY
    -                        break correctionLoop; // oops. Fell off end of range.
    -                    }
    -                    continue; // try again.
    -                }
    -
    -            }
    -            if (isNegative) {
    -                ieeeBits |= DoubleConsts.SIGN_BIT_MASK;
    -            }
    -            return Double.longBitsToDouble(ieeeBits);
    -        }
    -
    -        /**
    -         * Takes a FloatingDecimal, which we presumably just scanned in,
    -         * and finds out what its value is, as a float.
    -         * This is distinct from doubleValue() to avoid the extremely
    -         * unlikely case of a double rounding error, wherein the conversion
    -         * to double has one rounding error, and the conversion of that double
    -         * to a float has another rounding error, IN THE WRONG DIRECTION,
    -         * ( because of the preference to a zero low-order bit ).
    -         */
    -        @Override
    -        public float floatValue() {
    -            int kDigits = Math.min(nDigits, SINGLE_MAX_DECIMAL_DIGITS + 1);
    -            //
    -            // convert the lead kDigits to an integer.
    -            //
    -            int iValue = (int) digits[0] - (int) '0';
    -            for (int i = 1; i < kDigits; i++) {
    -                iValue = iValue * 10 + (int) digits[i] - (int) '0';
    -            }
    -            float fValue = (float) iValue;
    -            int exp = decExponent - kDigits;
    -            //
    -            // iValue now contains an integer with the value of
    -            // the first kDigits digits of the number.
    -            // fValue contains the (float) of the same.
    -            //
    -
    -            if (nDigits <= SINGLE_MAX_DECIMAL_DIGITS) {
    -                //
    -                // possibly an easy case.
    -                // We know that the digits can be represented
    -                // exactly. And if the exponent isn't too outrageous,
    -                // the whole thing can be done with one operation,
    -                // thus one rounding error.
    -                // Note that all our constructors trim all leading and
    -                // trailing zeros, so simple values (including zero)
    -                // will always end up here.
    -                //
    -                if (exp == 0 || fValue == 0.0f) {
    -                    return (isNegative) ? -fValue : fValue; // small floating integer
    -                } else if (exp >= 0) {
    -                    if (exp <= SINGLE_MAX_SMALL_TEN) {
    -                        //
    -                        // Can get the answer with one operation,
    -                        // thus one roundoff.
    -                        //
    -                        fValue *= SINGLE_SMALL_10_POW[exp];
    -                        return (isNegative) ? -fValue : fValue;
    -                    }
    -                    int slop = SINGLE_MAX_DECIMAL_DIGITS - kDigits;
    -                    if (exp <= SINGLE_MAX_SMALL_TEN + slop) {
    -                        //
    -                        // We can multiply fValue by 10^(slop)
    -                        // and it is still "small" and exact.
    -                        // Then we can multiply by 10^(exp-slop)
    -                        // with one rounding.
    -                        //
    -                        fValue *= SINGLE_SMALL_10_POW[slop];
    -                        fValue *= SINGLE_SMALL_10_POW[exp - slop];
    -                        return (isNegative) ? -fValue : fValue;
    -                    }
    -                    //
    -                    // Else we have a hard case with a positive exp.
    -                    //
    -                } else {
    -                    if (exp >= -SINGLE_MAX_SMALL_TEN) {
    -                        //
    -                        // Can get the answer in one division.
    -                        //
    -                        fValue /= SINGLE_SMALL_10_POW[-exp];
    -                        return (isNegative) ? -fValue : fValue;
    -                    }
    -                    //
    -                    // Else we have a hard case with a negative exp.
    -                    //
    -                }
    -            } else if ((decExponent >= nDigits) && (nDigits + decExponent <= MAX_DECIMAL_DIGITS)) {
    -                //
    -                // In double-precision, this is an exact floating integer.
    -                // So we can compute to double, then shorten to float
    -                // with one round, and get the right answer.
    -                //
    -                // First, finish accumulating digits.
    -                // Then convert that integer to a double, multiply
    -                // by the appropriate power of ten, and convert to float.
    -                //
    -                long lValue = (long) iValue;
    -                for (int i = kDigits; i < nDigits; i++) {
    -                    lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
    -                }
    -                double dValue = (double) lValue;
    -                exp = decExponent - nDigits;
    -                dValue *= SMALL_10_POW[exp];
    -                fValue = (float) dValue;
    -                return (isNegative) ? -fValue : fValue;
    -
    -            }
    -            //
    -            // Harder cases:
    -            // The sum of digits plus exponent is greater than
    -            // what we think we can do with one error.
    -            //
    -            // Start by approximating the right answer by,
    -            // naively, scaling by powers of 10.
    -            // Scaling uses doubles to avoid overflow/underflow.
    -            //
    -            double dValue = fValue;
    -            if (exp > 0) {
    -                if (decExponent > SINGLE_MAX_DECIMAL_EXPONENT + 1) {
    -                    //
    -                    // Lets face it. This is going to be
    -                    // Infinity. Cut to the chase.
    -                    //
    -                    return (isNegative) ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
    -                }
    -                if ((exp & 15) != 0) {
    -                    dValue *= SMALL_10_POW[exp & 15];
    -                }
    -                if ((exp >>= 4) != 0) {
    -                    int j;
    -                    for (j = 0; exp > 0; j++, exp >>= 1) {
    -                        if ((exp & 1) != 0) {
    -                            dValue *= BIG_10_POW[j];
    -                        }
    -                    }
    -                }
    -            } else if (exp < 0) {
    -                exp = -exp;
    -                if (decExponent < SINGLE_MIN_DECIMAL_EXPONENT - 1) {
    -                    //
    -                    // Lets face it. This is going to be
    -                    // zero. Cut to the chase.
    -                    //
    -                    return (isNegative) ? -0.0f : 0.0f;
    -                }
    -                if ((exp & 15) != 0) {
    -                    dValue /= SMALL_10_POW[exp & 15];
    -                }
    -                if ((exp >>= 4) != 0) {
    -                    int j;
    -                    for (j = 0; exp > 0; j++, exp >>= 1) {
    -                        if ((exp & 1) != 0) {
    -                            dValue *= TINY_10_POW[j];
    -                        }
    -                    }
    -                }
    -            }
    -            fValue = Math.max(Float.MIN_VALUE, Math.min(Float.MAX_VALUE, (float) dValue));
    -
    -            //
    -            // fValue is now approximately the result.
    -            // The hard part is adjusting it, by comparison
    -            // with FDBigInteger arithmetic.
    -            // Formulate the EXACT big-number result as
    -            // bigD0 * 10^exp
    -            //
    -            if (nDigits > SINGLE_MAX_NDIGITS) {
    -                nDigits = SINGLE_MAX_NDIGITS + 1;
    -                digits[SINGLE_MAX_NDIGITS] = '1';
    -            }
    -            FDBigInteger bigD0 = new FDBigInteger(iValue, digits, kDigits, nDigits);
    -            exp = decExponent - nDigits;
    -
    -            int ieeeBits = Float.floatToRawIntBits(fValue); // IEEE-754 bits of float candidate
    -            final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
    -            final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
    -            bigD0 = bigD0.multByPow52(D5, 0);
    -            bigD0.makeImmutable();   // prevent bigD0 modification inside correctionLoop
    -            FDBigInteger bigD = null;
    -            int prevD2 = 0;
    -
    -            correctionLoop:
    -            while (true) {
    -                // here ieeeBits can't be NaN, Infinity or zero
    -                int binexp = ieeeBits >>> SINGLE_EXP_SHIFT;
    -                int bigBbits = ieeeBits & FloatConsts.SIGNIF_BIT_MASK;
    -                if (binexp > 0) {
    -                    bigBbits |= SINGLE_FRACT_HOB;
    -                } else { // Normalize denormalized numbers.
    -                    assert bigBbits != 0 : bigBbits; // floatToBigInt(0.0)
    -                    int leadingZeros = Integer.numberOfLeadingZeros(bigBbits);
    -                    int shift = leadingZeros - (31 - SINGLE_EXP_SHIFT);
    -                    bigBbits <<= shift;
    -                    binexp = 1 - shift;
    -                }
    -                binexp -= FloatConsts.EXP_BIAS;
    -                int lowOrderZeros = Integer.numberOfTrailingZeros(bigBbits);
    -                bigBbits >>>= lowOrderZeros;
    -                final int bigIntExp = binexp - SINGLE_EXP_SHIFT + lowOrderZeros;
    -                final int bigIntNBits = SINGLE_EXP_SHIFT + 1 - lowOrderZeros;
    -
    -                //
    -                // Scale bigD, bigB appropriately for
    -                // big-integer operations.
    -                // Naively, we multiply by powers of ten
    -                // and powers of two. What we actually do
    -                // is keep track of the powers of 5 and
    -                // powers of 2 we would use, then factor out
    -                // common divisors before doing the work.
    -                //
    -                int B2 = B5; // powers of 2 in bigB
    -                int D2 = D5; // powers of 2 in bigD
    -                int Ulp2;   // powers of 2 in halfUlp.
    -                if (bigIntExp >= 0) {
    -                    B2 += bigIntExp;
    -                } else {
    -                    D2 -= bigIntExp;
    -                }
    -                Ulp2 = B2;
    -                // shift bigB and bigD left by a number s. t.
    -                // halfUlp is still an integer.
    -                int hulpbias;
    -                if (binexp <= -FloatConsts.EXP_BIAS) {
    -                    // This is going to be a denormalized number
    -                    // (if not actually zero).
    -                    // half an ULP is at 2^-(FloatConsts.EXP_BIAS+SINGLE_EXP_SHIFT+1)
    -                    hulpbias = binexp + lowOrderZeros + FloatConsts.EXP_BIAS;
    -                } else {
    -                    hulpbias = 1 + lowOrderZeros;
    -                }
    -                B2 += hulpbias;
    -                D2 += hulpbias;
    -                // if there are common factors of 2, we might just as well
    -                // factor them out, as they add nothing useful.
    -                int common2 = Math.min(B2, Math.min(D2, Ulp2));
    -                B2 -= common2;
    -                D2 -= common2;
    -                Ulp2 -= common2;
    -                // do multiplications by powers of 5 and 2
    -                FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
    -                if (bigD == null || prevD2 != D2) {
    -                    bigD = bigD0.leftShift(D2);
    -                    prevD2 = D2;
    -                }
    -                //
    -                // to recap:
    -                // bigB is the scaled-big-int version of our floating-point
    -                // candidate.
    -                // bigD is the scaled-big-int version of the exact value
    -                // as we understand it.
    -                // halfUlp is 1/2 an ulp of bigB, except for special cases
    -                // of exact powers of 2
    -                //
    -                // the plan is to compare bigB with bigD, and if the difference
    -                // is less than halfUlp, then we're satisfied. Otherwise,
    -                // use the ratio of difference to halfUlp to calculate a fudge
    -                // factor to add to the floating value, then go 'round again.
    -                //
    -                FDBigInteger diff;
    -                int cmpResult;
    -                boolean overvalue;
    -                if ((cmpResult = bigB.cmp(bigD)) > 0) {
    -                    overvalue = true; // our candidate is too big.
    -                    diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
    -                    if ((bigIntNBits == 1) && (bigIntExp > -FloatConsts.EXP_BIAS + 1)) {
    -                        // candidate is a normalized exact power of 2 and
    -                        // is too big (larger than Float.MIN_NORMAL). We will be subtracting.
    -                        // For our purposes, ulp is the ulp of the
    -                        // next smaller range.
    -                        Ulp2 -= 1;
    -                        if (Ulp2 < 0) {
    -                            // rats. Cannot de-scale ulp this far.
    -                            // must scale diff in other direction.
    -                            Ulp2 = 0;
    -                            diff = diff.leftShift(1);
    -                        }
    -                    }
    -                } else if (cmpResult < 0) {
    -                    overvalue = false; // our candidate is too small.
    -                    diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
    -                } else {
    -                    // the candidate is exactly right!
    -                    // this happens with surprising frequency
    -                    break correctionLoop;
    -                }
    -                cmpResult = diff.cmpPow52(B5, Ulp2);
    -                if ((cmpResult) < 0) {
    -                    // difference is small.
    -                    // this is close enough
    -                    break correctionLoop;
    -                } else if (cmpResult == 0) {
    -                    // difference is exactly half an ULP
    -                    // round to some other value maybe, then finish
    -                    if ((ieeeBits & 1) != 0) { // half ties to even
    -                        ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    -                    }
    -                    break correctionLoop;
    -                } else {
    -                    // difference is non-trivial.
    -                    // could scale addend by ratio of difference to
    -                    // halfUlp here, if we bothered to compute that difference.
    -                    // Most of the time ( I hope ) it is about 1 anyway.
    -                    ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    -                    if (ieeeBits == 0 || ieeeBits == FloatConsts.EXP_BIT_MASK) { // 0.0 or Float.POSITIVE_INFINITY
    -                        break correctionLoop; // oops. Fell off end of range.
    -                    }
    -                    continue; // try again.
    -                }
    -
    -            }
    -            if (isNegative) {
    -                ieeeBits |= FloatConsts.SIGN_BIT_MASK;
    -            }
    -            return Float.intBitsToFloat(ieeeBits);
    -        }
    -
    -
    -        /**
    -         * All the positive powers of 10 that can be
    -         * represented exactly in double/float.
    -         */
    -        private static final double[] SMALL_10_POW = {
    -            1.0e0,
    -            1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5,
    -            1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10,
    -            1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15,
    -            1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20,
    -            1.0e21, 1.0e22
    -        };
    -
    -        private static final float[] SINGLE_SMALL_10_POW = {
    -            1.0e0f,
    -            1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
    -            1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
    -        };
    -
    -        private static final double[] BIG_10_POW = {
    -            1e16, 1e32, 1e64, 1e128, 1e256 };
    -        private static final double[] TINY_10_POW = {
    -            1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
    -
    -        private static final int MAX_SMALL_TEN = SMALL_10_POW.length-1;
    -        private static final int SINGLE_MAX_SMALL_TEN = SINGLE_SMALL_10_POW.length-1;
    -
    -    }
    -
    -    /**
    -     * Returns a BinaryToASCIIConverter for a double.
    -     * The returned object is a ThreadLocal variable of this class.
    -     *
    -     * @param d The double precision value to convert.
    -     * @return The converter.
    -     */
    -    public static BinaryToASCIIConverter getBinaryToASCIIConverter(double d) {
    -        return getBinaryToASCIIConverter(d, true);
    -    }
    -
    -    /**
    -     * Returns a BinaryToASCIIConverter for a double.
    -     * The returned object is a ThreadLocal variable of this class.
    -     *
    -     * @param d The double precision value to convert.
    -     * @param isCompatibleFormat
    -     * @return The converter.
    -     */
    -    static BinaryToASCIIConverter getBinaryToASCIIConverter(double d, boolean isCompatibleFormat) {
    -        long dBits = Double.doubleToRawLongBits(d);
    -        boolean isNegative = (dBits&DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign
    -        long fractBits = dBits & DoubleConsts.SIGNIF_BIT_MASK;
    -        int  binExp = (int)( (dBits&DoubleConsts.EXP_BIT_MASK) >> EXP_SHIFT );
    -        // Discover obvious special cases of NaN and Infinity.
    -        if ( binExp == (int)(DoubleConsts.EXP_BIT_MASK>>EXP_SHIFT) ) {
    -            if ( fractBits == 0L ){
    -                return isNegative ? B2AC_NEGATIVE_INFINITY : B2AC_POSITIVE_INFINITY;
    -            } else {
    -                return B2AC_NOT_A_NUMBER;
    -            }
    -        }
    -        // Finish unpacking
    -        // Normalize denormalized numbers.
    -        // Insert assumed high-order bit for normalized numbers.
    -        // Subtract exponent bias.
    -        int  nSignificantBits;
    -        if ( binExp == 0 ){
    -            if ( fractBits == 0L ){
    -                // not a denorm, just a 0!
    -                return isNegative ? B2AC_NEGATIVE_ZERO : B2AC_POSITIVE_ZERO;
    -            }
    -            int leadingZeros = Long.numberOfLeadingZeros(fractBits);
    -            int shift = leadingZeros-(63-EXP_SHIFT);
    -            fractBits <<= shift;
    -            binExp = 1 - shift;
    -            nSignificantBits =  64-leadingZeros; // recall binExp is  - shift count.
    -        } else {
    -            fractBits |= FRACT_HOB;
    -            nSignificantBits = EXP_SHIFT+1;
    -        }
    -        binExp -= DoubleConsts.EXP_BIAS;
    -        BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
    -        buf.setSign(isNegative);
    -        // call the routine that actually does all the hard work.
    -        buf.dtoa(binExp, fractBits, nSignificantBits, isCompatibleFormat);
    -        return buf;
    -    }
    -
    -    private static BinaryToASCIIConverter getBinaryToASCIIConverter(float f) {
    -        int fBits = Float.floatToRawIntBits( f );
    -        boolean isNegative = (fBits&FloatConsts.SIGN_BIT_MASK) != 0;
    -        int fractBits = fBits&FloatConsts.SIGNIF_BIT_MASK;
    -        int binExp = (fBits&FloatConsts.EXP_BIT_MASK) >> SINGLE_EXP_SHIFT;
    -        // Discover obvious special cases of NaN and Infinity.
    -        if ( binExp == (FloatConsts.EXP_BIT_MASK>>SINGLE_EXP_SHIFT) ) {
    -            if ( fractBits == 0L ){
    -                return isNegative ? B2AC_NEGATIVE_INFINITY : B2AC_POSITIVE_INFINITY;
    -            } else {
    -                return B2AC_NOT_A_NUMBER;
    -            }
    -        }
    -        // Finish unpacking
    -        // Normalize denormalized numbers.
    -        // Insert assumed high-order bit for normalized numbers.
    -        // Subtract exponent bias.
    -        int  nSignificantBits;
    -        if ( binExp == 0 ){
    -            if ( fractBits == 0 ){
    -                // not a denorm, just a 0!
    -                return isNegative ? B2AC_NEGATIVE_ZERO : B2AC_POSITIVE_ZERO;
    -            }
    -            int leadingZeros = Integer.numberOfLeadingZeros(fractBits);
    -            int shift = leadingZeros-(31-SINGLE_EXP_SHIFT);
    -            fractBits <<= shift;
    -            binExp = 1 - shift;
    -            nSignificantBits =  32 - leadingZeros; // recall binExp is  - shift count.
    -        } else {
    -            fractBits |= SINGLE_FRACT_HOB;
    -            nSignificantBits = SINGLE_EXP_SHIFT+1;
    -        }
    -        binExp -= FloatConsts.EXP_BIAS;
    -        BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
    -        buf.setSign(isNegative);
    -        // call the routine that actually does all the hard work.
    -        buf.dtoa(binExp, ((long)fractBits)<<(EXP_SHIFT-SINGLE_EXP_SHIFT), nSignificantBits, true);
    -        return buf;
    -    }
    -
    -    @SuppressWarnings("fallthrough")
    -    static ASCIIToBinaryConverter readJavaFormatString( String in ) throws NumberFormatException {
    -        boolean isNegative = false;
    -        boolean signSeen   = false;
    -        int     decExp;
    -        char    c;
    -
    -    parseNumber:
    -        try{
    -            in = in.trim(); // don't fool around with white space.
    -                            // throws NullPointerException if null
    -            int len = in.length();
    -            if ( len == 0 ) {
    -                throw new NumberFormatException("empty String");
    -            }
    -            int i = 0;
    -            switch (in.charAt(i)){
    -            case '-':
    -                isNegative = true;
    -                //FALLTHROUGH
    -            case '+':
    -                i++;
    -                signSeen = true;
    -            }
    -            c = in.charAt(i);
    -            if(c == 'N') { // Check for NaN
    -                if((len-i)==NAN_LENGTH && in.indexOf(NAN_REP,i)==i) {
    -                    return A2BC_NOT_A_NUMBER;
    -                }
    -                // something went wrong, throw exception
    -                break parseNumber;
    -            } else if(c == 'I') { // Check for Infinity strings
    -                if((len-i)==INFINITY_LENGTH && in.indexOf(INFINITY_REP,i)==i) {
    -                    return isNegative? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
    -                }
    -                // something went wrong, throw exception
    -                break parseNumber;
    -            } else if (c == '0')  { // check for hexadecimal floating-point number
    -                if (len > i+1 ) {
    -                    char ch = in.charAt(i+1);
    -                    if (ch == 'x' || ch == 'X' ) { // possible hex string
    -                        return parseHexString(in);
    -                    }
    -                }
    -            }  // look for and process decimal floating-point string
    -
    -            char[] digits = new char[ len ];
    -            int    nDigits= 0;
    -            boolean decSeen = false;
    -            int decPt = 0;
    -            int nLeadZero = 0;
    -            int nTrailZero= 0;
    -
    -        skipLeadingZerosLoop:
    -            while (i < len) {
    -                c = in.charAt(i);
    -                if (c == '0') {
    -                    nLeadZero++;
    -                } else if (c == '.') {
    -                    if (decSeen) {
    -                        // already saw one ., this is the 2nd.
    -                        throw new NumberFormatException("multiple points");
    -                    }
    -                    decPt = i;
    -                    if (signSeen) {
    -                        decPt -= 1;
    -                    }
    -                    decSeen = true;
    -                } else {
    -                    break skipLeadingZerosLoop;
    -                }
    -                i++;
    -            }
    -        digitLoop:
    -            while (i < len) {
    -                c = in.charAt(i);
    -                if (c >= '1' && c <= '9') {
    -                    digits[nDigits++] = c;
    -                    nTrailZero = 0;
    -                } else if (c == '0') {
    -                    digits[nDigits++] = c;
    -                    nTrailZero++;
    -                } else if (c == '.') {
    -                    if (decSeen) {
    -                        // already saw one ., this is the 2nd.
    -                        throw new NumberFormatException("multiple points");
    -                    }
    -                    decPt = i;
    -                    if (signSeen) {
    -                        decPt -= 1;
    -                    }
    -                    decSeen = true;
    -                } else {
    -                    break digitLoop;
    -                }
    -                i++;
    -            }
    -            nDigits -=nTrailZero;
    -            //
    -            // At this point, we've scanned all the digits and decimal
    -            // point we're going to see. Trim off leading and trailing
    -            // zeros, which will just confuse us later, and adjust
    -            // our initial decimal exponent accordingly.
    -            // To review:
    -            // we have seen i total characters.
    -            // nLeadZero of them were zeros before any other digits.
    -            // nTrailZero of them were zeros after any other digits.
    -            // if ( decSeen ), then a . was seen after decPt characters
    -            // ( including leading zeros which have been discarded )
    -            // nDigits characters were neither lead nor trailing
    -            // zeros, nor point
    -            //
    -            //
    -            // special hack: if we saw no non-zero digits, then the
    -            // answer is zero!
    -            // Unfortunately, we feel honor-bound to keep parsing!
    -            //
    -            boolean isZero = (nDigits == 0);
    -            if ( isZero &&  nLeadZero == 0 ){
    -                // we saw NO DIGITS AT ALL,
    -                // not even a crummy 0!
    -                // this is not allowed.
    -                break parseNumber; // go throw exception
    -            }
    -            //
    -            // Our initial exponent is decPt, adjusted by the number of
    -            // discarded zeros. Or, if there was no decPt,
    -            // then its just nDigits adjusted by discarded trailing zeros.
    -            //
    -            if ( decSeen ){
    -                decExp = decPt - nLeadZero;
    -            } else {
    -                decExp = nDigits + nTrailZero;
    -            }
    -
    -            //
    -            // Look for 'e' or 'E' and an optionally signed integer.
    -            //
    -            if ( (i < len) &&  (((c = in.charAt(i) )=='e') || (c == 'E') ) ){
    -                int expSign = 1;
    -                int expVal  = 0;
    -                int reallyBig = Integer.MAX_VALUE / 10;
    -                boolean expOverflow = false;
    -                switch( in.charAt(++i) ){
    -                case '-':
    -                    expSign = -1;
    -                    //FALLTHROUGH
    -                case '+':
    -                    i++;
    -                }
    -                int expAt = i;
    -            expLoop:
    -                while ( i < len  ){
    -                    if ( expVal >= reallyBig ){
    -                        // the next character will cause integer
    -                        // overflow.
    -                        expOverflow = true;
    -                    }
    -                    c = in.charAt(i++);
    -                    if(c>='0' && c<='9') {
    -                        expVal = expVal*10 + ( (int)c - (int)'0' );
    -                    } else {
    -                        i--;           // back up.
    -                        break expLoop; // stop parsing exponent.
    -                    }
    -                }
    -                int expLimit = BIG_DECIMAL_EXPONENT + nDigits + nTrailZero;
    -                if (expOverflow || (expVal > expLimit)) {
    -                    // There is still a chance that the exponent will be safe to
    -                    // use: if it would eventually decrease due to a negative
    -                    // decExp, and that number is below the limit.  We check for
    -                    // that here.
    -                    if (!expOverflow && (expSign == 1 && decExp < 0)
    -                            && (expVal + decExp) < expLimit) {
    -                        // Cannot overflow: adding a positive and negative number.
    -                        decExp += expVal;
    -                    } else {
    -                        //
    -                        // The intent here is to end up with
    -                        // infinity or zero, as appropriate.
    -                        // The reason for yielding such a small decExponent,
    -                        // rather than something intuitive such as
    -                        // expSign*Integer.MAX_VALUE, is that this value
    -                        // is subject to further manipulation in
    -                        // doubleValue() and floatValue(), and I don't want
    -                        // it to be able to cause overflow there!
    -                        // (The only way we can get into trouble here is for
    -                        // really outrageous nDigits+nTrailZero, such as 2
    -                        // billion.)
    -                        //
    -                        decExp = expSign * expLimit;
    -                    }
    -                } else {
    -                    // this should not overflow, since we tested
    -                    // for expVal > (MAX+N), where N >= abs(decExp)
    -                    decExp = decExp + expSign*expVal;
    -                }
    -
    -                // if we saw something not a digit ( or end of string )
    -                // after the [Ee][+-], without seeing any digits at all
    -                // this is certainly an error. If we saw some digits,
    -                // but then some trailing garbage, that might be ok.
    -                // so we just fall through in that case.
    -                // HUMBUG
    -                if ( i == expAt ) {
    -                    break parseNumber; // certainly bad
    -                }
    -            }
    -            //
    -            // We parsed everything we could.
    -            // If there are leftovers, then this is not good input!
    -            //
    -            if ( i < len &&
    -                ((i != len - 1) ||
    -                (in.charAt(i) != 'f' &&
    -                 in.charAt(i) != 'F' &&
    -                 in.charAt(i) != 'd' &&
    -                 in.charAt(i) != 'D'))) {
    -                break parseNumber; // go throw exception
    -            }
    -            if(isZero) {
    -                return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
    -            }
    -            return new ASCIIToBinaryBuffer(isNegative, decExp, digits, nDigits);
    -        } catch ( StringIndexOutOfBoundsException e ){ }
    -        throw new NumberFormatException("For input string: \"" + in + "\"");
    -    }
    -
    -    private static class HexFloatPattern {
    -        /**
    -         * Grammar is compatible with hexadecimal floating-point constants
    -         * described in section 6.4.4.2 of the C99 specification.
    -         */
    -        private static final Pattern VALUE = Pattern.compile(
    -                   //1           234                   56                7                   8      9
    -                    "([-+])?0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?"
    -                    );
    -    }
    -
    -    /**
    -     * Converts string s to a suitable floating decimal; uses the
    -     * double constructor and sets the roundDir variable appropriately
    -     * in case the value is later converted to a float.
    -     *
    -     * @param s The String to parse.
    -     */
    -   static ASCIIToBinaryConverter parseHexString(String s) {
    -            // Verify string is a member of the hexadecimal floating-point
    -            // string language.
    -            Matcher m = HexFloatPattern.VALUE.matcher(s);
    -            boolean validInput = m.matches();
    -            if (!validInput) {
    -                // Input does not match pattern
    -                throw new NumberFormatException("For input string: \"" + s + "\"");
    -            } else { // validInput
    -                //
    -                // We must isolate the sign, significand, and exponent
    -                // fields.  The sign value is straightforward.  Since
    -                // floating-point numbers are stored with a normalized
    -                // representation, the significand and exponent are
    -                // interrelated.
    -                //
    -                // After extracting the sign, we normalized the
    -                // significand as a hexadecimal value, calculating an
    -                // exponent adjust for any shifts made during
    -                // normalization.  If the significand is zero, the
    -                // exponent doesn't need to be examined since the output
    -                // will be zero.
    -                //
    -                // Next the exponent in the input string is extracted.
    -                // Afterwards, the significand is normalized as a *binary*
    -                // value and the input value's normalized exponent can be
    -                // computed.  The significand bits are copied into a
    -                // double significand; if the string has more logical bits
    -                // than can fit in a double, the extra bits affect the
    -                // round and sticky bits which are used to round the final
    -                // value.
    -                //
    -                //  Extract significand sign
    -                String group1 = m.group(1);
    -                boolean isNegative = ((group1 != null) && group1.equals("-"));
    -
    -                //  Extract Significand magnitude
    -                //
    -                // Based on the form of the significand, calculate how the
    -                // binary exponent needs to be adjusted to create a
    -                // normalized//hexadecimal* floating-point number; that
    -                // is, a number where there is one nonzero hex digit to
    -                // the left of the (hexa)decimal point.  Since we are
    -                // adjusting a binary, not hexadecimal exponent, the
    -                // exponent is adjusted by a multiple of 4.
    -                //
    -                // There are a number of significand scenarios to consider;
    -                // letters are used in indicate nonzero digits:
    -                //
    -                // 1. 000xxxx       =>      x.xxx   normalized
    -                //    increase exponent by (number of x's - 1)*4
    -                //
    -                // 2. 000xxx.yyyy =>        x.xxyyyy        normalized
    -                //    increase exponent by (number of x's - 1)*4
    -                //
    -                // 3. .000yyy  =>   y.yy    normalized
    -                //    decrease exponent by (number of zeros + 1)*4
    -                //
    -                // 4. 000.00000yyy => y.yy normalized
    -                //    decrease exponent by (number of zeros to right of point + 1)*4
    -                //
    -                // If the significand is exactly zero, return a properly
    -                // signed zero.
    -                //
    -
    -                String significandString = null;
    -                int signifLength = 0;
    -                int exponentAdjust = 0;
    -                {
    -                    int leftDigits = 0; // number of meaningful digits to
    -                    // left of "decimal" point
    -                    // (leading zeros stripped)
    -                    int rightDigits = 0; // number of digits to right of
    -                    // "decimal" point; leading zeros
    -                    // must always be accounted for
    -                    //
    -                    // The significand is made up of either
    -                    //
    -                    // 1. group 4 entirely (integer portion only)
    -                    //
    -                    // OR
    -                    //
    -                    // 2. the fractional portion from group 7 plus any
    -                    // (optional) integer portions from group 6.
    -                    //
    -                    String group4;
    -                    if ((group4 = m.group(4)) != null) {  // Integer-only significand
    -                        // Leading zeros never matter on the integer portion
    -                        significandString = stripLeadingZeros(group4);
    -                        leftDigits = significandString.length();
    -                    } else {
    -                        // Group 6 is the optional integer; leading zeros
    -                        // never matter on the integer portion
    -                        String group6 = stripLeadingZeros(m.group(6));
    -                        leftDigits = group6.length();
    -
    -                        // fraction
    -                        String group7 = m.group(7);
    -                        rightDigits = group7.length();
    -
    -                        // Turn "integer.fraction" into "integer"+"fraction"
    -                        significandString =
    -                                ((group6 == null) ? "" : group6) + // is the null
    -                                        // check necessary?
    -                                        group7;
    -                    }
    -
    -                    significandString = stripLeadingZeros(significandString);
    -                    signifLength = significandString.length();
    -
    -                    //
    -                    // Adjust exponent as described above
    -                    //
    -                    if (leftDigits >= 1) {  // Cases 1 and 2
    -                        exponentAdjust = 4 * (leftDigits - 1);
    -                    } else {                // Cases 3 and 4
    -                        exponentAdjust = -4 * (rightDigits - signifLength + 1);
    -                    }
    -
    -                    // If the significand is zero, the exponent doesn't
    -                    // matter; return a properly signed zero.
    -
    -                    if (signifLength == 0) { // Only zeros in input
    -                        return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
    -                    }
    -                }
    -
    -                //  Extract Exponent
    -                //
    -                // Use an int to read in the exponent value; this should
    -                // provide more than sufficient range for non-contrived
    -                // inputs.  If reading the exponent in as an int does
    -                // overflow, examine the sign of the exponent and
    -                // significand to determine what to do.
    -                //
    -                String group8 = m.group(8);
    -                boolean positiveExponent = (group8 == null) || group8.equals("+");
    -                long unsignedRawExponent;
    -                try {
    -                    unsignedRawExponent = Integer.parseInt(m.group(9));
    -                }
    -                catch (NumberFormatException e) {
    -                    // At this point, we know the exponent is
    -                    // syntactically well-formed as a sequence of
    -                    // digits.  Therefore, if an NumberFormatException
    -                    // is thrown, it must be due to overflowing int's
    -                    // range.  Also, at this point, we have already
    -                    // checked for a zero significand.  Thus the signs
    -                    // of the exponent and significand determine the
    -                    // final result:
    -                    //
    -                    //                      significand
    -                    //                      +               -
    -                    // exponent     +       +infinity       -infinity
    -                    //              -       +0.0            -0.0
    -                    return isNegative ?
    -                              (positiveExponent ? A2BC_NEGATIVE_INFINITY : A2BC_NEGATIVE_ZERO)
    -                            : (positiveExponent ? A2BC_POSITIVE_INFINITY : A2BC_POSITIVE_ZERO);
    -
    -                }
    -
    -                long rawExponent =
    -                        (positiveExponent ? 1L : -1L) * // exponent sign
    -                                unsignedRawExponent;            // exponent magnitude
    -
    -                // Calculate partially adjusted exponent
    -                long exponent = rawExponent + exponentAdjust;
    -
    -                // Starting copying non-zero bits into proper position in
    -                // a long; copy explicit bit too; this will be masked
    -                // later for normal values.
    -
    -                boolean round = false;
    -                boolean sticky = false;
    -                int nextShift = 0;
    -                long significand = 0L;
    -                // First iteration is different, since we only copy
    -                // from the leading significand bit; one more exponent
    -                // adjust will be needed...
    -
    -                // IMPORTANT: make leadingDigit a long to avoid
    -                // surprising shift semantics!
    -                long leadingDigit = getHexDigit(significandString, 0);
    -
    -                //
    -                // Left shift the leading digit (53 - (bit position of
    -                // leading 1 in digit)); this sets the top bit of the
    -                // significand to 1.  The nextShift value is adjusted
    -                // to take into account the number of bit positions of
    -                // the leadingDigit actually used.  Finally, the
    -                // exponent is adjusted to normalize the significand
    -                // as a binary value, not just a hex value.
    -                //
    -                if (leadingDigit == 1) {
    -                    significand |= leadingDigit << 52;
    -                    nextShift = 52 - 4;
    -                    // exponent += 0
    -                } else if (leadingDigit <= 3) { // [2, 3]
    -                    significand |= leadingDigit << 51;
    -                    nextShift = 52 - 5;
    -                    exponent += 1;
    -                } else if (leadingDigit <= 7) { // [4, 7]
    -                    significand |= leadingDigit << 50;
    -                    nextShift = 52 - 6;
    -                    exponent += 2;
    -                } else if (leadingDigit <= 15) { // [8, f]
    -                    significand |= leadingDigit << 49;
    -                    nextShift = 52 - 7;
    -                    exponent += 3;
    -                } else {
    -                    throw new AssertionError("Result from digit conversion too large!");
    -                }
    -                // The preceding if-else could be replaced by a single
    -                // code block based on the high-order bit set in
    -                // leadingDigit.  Given leadingOnePosition,
    -
    -                // significand |= leadingDigit << (SIGNIFICAND_WIDTH - leadingOnePosition);
    -                // nextShift = 52 - (3 + leadingOnePosition);
    -                // exponent += (leadingOnePosition-1);
    -
    -                //
    -                // Now the exponent variable is equal to the normalized
    -                // binary exponent.  Code below will make representation
    -                // adjustments if the exponent is incremented after
    -                // rounding (includes overflows to infinity) or if the
    -                // result is subnormal.
    -                //
    -
    -                // Copy digit into significand until the significand can't
    -                // hold another full hex digit or there are no more input
    -                // hex digits.
    -                int i = 0;
    -                for (i = 1;
    -                     i < signifLength && nextShift >= 0;
    -                     i++) {
    -                    long currentDigit = getHexDigit(significandString, i);
    -                    significand |= (currentDigit << nextShift);
    -                    nextShift -= 4;
    -                }
    -
    -                // After the above loop, the bulk of the string is copied.
    -                // Now, we must copy any partial hex digits into the
    -                // significand AND compute the round bit and start computing
    -                // sticky bit.
    -
    -                if (i < signifLength) { // at least one hex input digit exists
    -                    long currentDigit = getHexDigit(significandString, i);
    -
    -                    // from nextShift, figure out how many bits need
    -                    // to be copied, if any
    -                    switch (nextShift) { // must be negative
    -                        case -1:
    -                            // three bits need to be copied in; can
    -                            // set round bit
    -                            significand |= ((currentDigit & 0xEL) >> 1);
    -                            round = (currentDigit & 0x1L) != 0L;
    -                            break;
    -
    -                        case -2:
    -                            // two bits need to be copied in; can
    -                            // set round and start sticky
    -                            significand |= ((currentDigit & 0xCL) >> 2);
    -                            round = (currentDigit & 0x2L) != 0L;
    -                            sticky = (currentDigit & 0x1L) != 0;
    -                            break;
    -
    -                        case -3:
    -                            // one bit needs to be copied in
    -                            significand |= ((currentDigit & 0x8L) >> 3);
    -                            // Now set round and start sticky, if possible
    -                            round = (currentDigit & 0x4L) != 0L;
    -                            sticky = (currentDigit & 0x3L) != 0;
    -                            break;
    -
    -                        case -4:
    -                            // all bits copied into significand; set
    -                            // round and start sticky
    -                            round = ((currentDigit & 0x8L) != 0);  // is top bit set?
    -                            // nonzeros in three low order bits?
    -                            sticky = (currentDigit & 0x7L) != 0;
    -                            break;
    -
    -                        default:
    -                            throw new AssertionError("Unexpected shift distance remainder.");
    -                            // break;
    -                    }
    -
    -                    // Round is set; sticky might be set.
    -
    -                    // For the sticky bit, it suffices to check the
    -                    // current digit and test for any nonzero digits in
    -                    // the remaining unprocessed input.
    -                    i++;
    -                    while (i < signifLength && !sticky) {
    -                        currentDigit = getHexDigit(significandString, i);
    -                        sticky = sticky || (currentDigit != 0);
    -                        i++;
    -                    }
    -
    -                }
    -                // else all of string was seen, round and sticky are
    -                // correct as false.
    -
    -                // Float calculations
    -                int floatBits = isNegative ? FloatConsts.SIGN_BIT_MASK : 0;
    -                if (exponent >= FloatConsts.MIN_EXPONENT) {
    -                    if (exponent > FloatConsts.MAX_EXPONENT) {
    -                        // Float.POSITIVE_INFINITY
    -                        floatBits |= FloatConsts.EXP_BIT_MASK;
    -                    } else {
    -                        int threshShift = DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH - 1;
    -                        boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky;
    -                        int iValue = (int) (significand >>> threshShift);
    -                        if ((iValue & 3) != 1 || floatSticky) {
    -                            iValue++;
    -                        }
    -                        floatBits |= (((((int) exponent) + (FloatConsts.EXP_BIAS - 1))) << SINGLE_EXP_SHIFT) + (iValue >> 1);
    -                    }
    -                } else {
    -                    if (exponent < FloatConsts.MIN_SUB_EXPONENT - 1) {
    -                        // 0
    -                    } else {
    -                        // exponent == -127 ==> threshShift = 53 - 2 + (-149) - (-127) = 53 - 24
    -                        int threshShift = (int) ((DoubleConsts.SIGNIFICAND_WIDTH - 2 + FloatConsts.MIN_SUB_EXPONENT) - exponent);
    -                        assert threshShift >= DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH;
    -                        assert threshShift < DoubleConsts.SIGNIFICAND_WIDTH;
    -                        boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky;
    -                        int iValue = (int) (significand >>> threshShift);
    -                        if ((iValue & 3) != 1 || floatSticky) {
    -                            iValue++;
    -                        }
    -                        floatBits |= iValue >> 1;
    -                    }
    -                }
    -                float fValue = Float.intBitsToFloat(floatBits);
    -
    -                // Check for overflow and update exponent accordingly.
    -                if (exponent > DoubleConsts.MAX_EXPONENT) {         // Infinite result
    -                    // overflow to properly signed infinity
    -                    return isNegative ? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
    -                } else {  // Finite return value
    -                    if (exponent <= DoubleConsts.MAX_EXPONENT && // (Usually) normal result
    -                            exponent >= DoubleConsts.MIN_EXPONENT) {
    -
    -                        // The result returned in this block cannot be a
    -                        // zero or subnormal; however after the
    -                        // significand is adjusted from rounding, we could
    -                        // still overflow in infinity.
    -
    -                        // AND exponent bits into significand; if the
    -                        // significand is incremented and overflows from
    -                        // rounding, this combination will update the
    -                        // exponent correctly, even in the case of
    -                        // Double.MAX_VALUE overflowing to infinity.
    -
    -                        significand = ((( exponent +
    -                                (long) DoubleConsts.EXP_BIAS) <<
    -                                (DoubleConsts.SIGNIFICAND_WIDTH - 1))
    -                                & DoubleConsts.EXP_BIT_MASK) |
    -                                (DoubleConsts.SIGNIF_BIT_MASK & significand);
    -
    -                    } else {  // Subnormal or zero
    -                        // (exponent < DoubleConsts.MIN_EXPONENT)
    -
    -                        if (exponent < (DoubleConsts.MIN_SUB_EXPONENT - 1)) {
    -                            // No way to round back to nonzero value
    -                            // regardless of significand if the exponent is
    -                            // less than -1075.
    -                            return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
    -                        } else { //  -1075 <= exponent <= MIN_EXPONENT -1 = -1023
    -                            //
    -                            // Find bit position to round to; recompute
    -                            // round and sticky bits, and shift
    -                            // significand right appropriately.
    -                            //
    -
    -                            sticky = sticky || round;
    -                            round = false;
    -
    -                            // Number of bits of significand to preserve is
    -                            // exponent - abs_min_exp +1
    -                            // check:
    -                            // -1075 +1074 + 1 = 0
    -                            // -1023 +1074 + 1 = 52
    -
    -                            int bitsDiscarded = 53 -
    -                                    ((int) exponent - DoubleConsts.MIN_SUB_EXPONENT + 1);
    -                            assert bitsDiscarded >= 1 && bitsDiscarded <= 53;
    -
    -                            // What to do here:
    -                            // First, isolate the new round bit
    -                            round = (significand & (1L << (bitsDiscarded - 1))) != 0L;
    -                            if (bitsDiscarded > 1) {
    -                                // create mask to update sticky bits; low
    -                                // order bitsDiscarded bits should be 1
    -                                long mask = ~((~0L) << (bitsDiscarded - 1));
    -                                sticky = sticky || ((significand & mask) != 0L);
    -                            }
    -
    -                            // Now, discard the bits
    -                            significand = significand >> bitsDiscarded;
    -
    -                            significand = ((((long) (DoubleConsts.MIN_EXPONENT - 1) + // subnorm exp.
    -                                    (long) DoubleConsts.EXP_BIAS) <<
    -                                    (DoubleConsts.SIGNIFICAND_WIDTH - 1))
    -                                    & DoubleConsts.EXP_BIT_MASK) |
    -                                    (DoubleConsts.SIGNIF_BIT_MASK & significand);
    -                        }
    -                    }
    -
    -                    // The significand variable now contains the currently
    -                    // appropriate exponent bits too.
    -
    -                    //
    -                    // Determine if significand should be incremented;
    -                    // making this determination depends on the least
    -                    // significant bit and the round and sticky bits.
    -                    //
    -                    // Round to nearest even rounding table, adapted from
    -                    // table 4.7 in "Computer Arithmetic" by IsraelKoren.
    -                    // The digit to the left of the "decimal" point is the
    -                    // least significant bit, the digits to the right of
    -                    // the point are the round and sticky bits
    -                    //
    -                    // Number       Round(x)
    -                    // x0.00        x0.
    -                    // x0.01        x0.
    -                    // x0.10        x0.
    -                    // x0.11        x1. = x0. +1
    -                    // x1.00        x1.
    -                    // x1.01        x1.
    -                    // x1.10        x1. + 1
    -                    // x1.11        x1. + 1
    -                    //
    -                    boolean leastZero = ((significand & 1L) == 0L);
    -                    if ((leastZero && round && sticky) ||
    -                            ((!leastZero) && round)) {
    -                        significand++;
    -                    }
    -
    -                    double value = isNegative ?
    -                            Double.longBitsToDouble(significand | DoubleConsts.SIGN_BIT_MASK) :
    -                            Double.longBitsToDouble(significand );
    -
    -                    return new PreparedASCIIToBinaryBuffer(value, fValue);
    -                }
    -            }
    -    }
    -
    -    /**
    -     * Returns s with any leading zeros removed.
    -     */
    -    static String stripLeadingZeros(String s) {
    -//        return  s.replaceFirst("^0+", "");
    -        if(!s.isEmpty() && s.charAt(0)=='0') {
    -            for(int i=1; iposition
    -     * of string s.
    -     */
    -    static int getHexDigit(String s, int position) {
    -        int value = Character.digit(s.charAt(position), 16);
    -        if (value <= -1 || value >= 16) {
    -            throw new AssertionError("Unexpected failure of digit conversion of " +
    -                                     s.charAt(position));
    -        }
    -        return value;
    -    }
    -}
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/misc/FormattedFloatingDecimal.java
    --- a/jdk/src/java.base/share/classes/sun/misc/FormattedFloatingDecimal.java	Wed Dec 23 15:41:50 2015 -0800
    +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    @@ -1,349 +0,0 @@
    -/*
    - * Copyright (c) 2003, 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 sun.misc;
    -
    -import java.util.Arrays;
    -
    -public class FormattedFloatingDecimal{
    -
    -    public enum Form { SCIENTIFIC, COMPATIBLE, DECIMAL_FLOAT, GENERAL };
    -
    -
    -    public static FormattedFloatingDecimal valueOf(double d, int precision, Form form){
    -        FloatingDecimal.BinaryToASCIIConverter fdConverter =
    -                FloatingDecimal.getBinaryToASCIIConverter(d, form == Form.COMPATIBLE);
    -        return new FormattedFloatingDecimal(precision,form, fdConverter);
    -    }
    -
    -    private int decExponentRounded;
    -    private char[] mantissa;
    -    private char[] exponent;
    -
    -    private static final ThreadLocal threadLocalCharBuffer =
    -            new ThreadLocal() {
    -                @Override
    -                protected Object initialValue() {
    -                    return new char[20];
    -                }
    -            };
    -
    -    private static char[] getBuffer(){
    -        return (char[]) threadLocalCharBuffer.get();
    -    }
    -
    -    private FormattedFloatingDecimal(int precision, Form form, FloatingDecimal.BinaryToASCIIConverter fdConverter) {
    -        if (fdConverter.isExceptional()) {
    -            this.mantissa = fdConverter.toJavaFormatString().toCharArray();
    -            this.exponent = null;
    -            return;
    -        }
    -        char[] digits = getBuffer();
    -        int nDigits = fdConverter.getDigits(digits);
    -        int decExp = fdConverter.getDecimalExponent();
    -        int exp;
    -        boolean isNegative = fdConverter.isNegative();
    -        switch (form) {
    -            case COMPATIBLE:
    -                exp = decExp;
    -                this.decExponentRounded = exp;
    -                fillCompatible(precision, digits, nDigits, exp, isNegative);
    -                break;
    -            case DECIMAL_FLOAT:
    -                exp = applyPrecision(decExp, digits, nDigits, decExp + precision);
    -                fillDecimal(precision, digits, nDigits, exp, isNegative);
    -                this.decExponentRounded = exp;
    -                break;
    -            case SCIENTIFIC:
    -                exp = applyPrecision(decExp, digits, nDigits, precision + 1);
    -                fillScientific(precision, digits, nDigits, exp, isNegative);
    -                this.decExponentRounded = exp;
    -                break;
    -            case GENERAL:
    -                exp = applyPrecision(decExp, digits, nDigits, precision);
    -                // adjust precision to be the number of digits to right of decimal
    -                // the real exponent to be output is actually exp - 1, not exp
    -                if (exp - 1 < -4 || exp - 1 >= precision) {
    -                    // form = Form.SCIENTIFIC;
    -                    precision--;
    -                    fillScientific(precision, digits, nDigits, exp, isNegative);
    -                } else {
    -                    // form = Form.DECIMAL_FLOAT;
    -                    precision = precision - exp;
    -                    fillDecimal(precision, digits, nDigits, exp, isNegative);
    -                }
    -                this.decExponentRounded = exp;
    -                break;
    -            default:
    -                assert false;
    -        }
    -    }
    -
    -    // returns the exponent after rounding has been done by applyPrecision
    -    public int getExponentRounded() {
    -        return decExponentRounded - 1;
    -    }
    -
    -    public char[] getMantissa(){
    -        return mantissa;
    -    }
    -
    -    public char[] getExponent(){
    -        return exponent;
    -    }
    -
    -    /**
    -     * Returns new decExp in case of overflow.
    -     */
    -    private static int applyPrecision(int decExp, char[] digits, int nDigits, int prec) {
    -        if (prec >= nDigits || prec < 0) {
    -            // no rounding necessary
    -            return decExp;
    -        }
    -        if (prec == 0) {
    -            // only one digit (0 or 1) is returned because the precision
    -            // excludes all significant digits
    -            if (digits[0] >= '5') {
    -                digits[0] = '1';
    -                Arrays.fill(digits, 1, nDigits, '0');
    -                return decExp + 1;
    -            } else {
    -                Arrays.fill(digits, 0, nDigits, '0');
    -                return decExp;
    -            }
    -        }
    -        int q = digits[prec];
    -        if (q >= '5') {
    -            int i = prec;
    -            q = digits[--i];
    -            if ( q == '9' ) {
    -                while ( q == '9' && i > 0 ){
    -                    q = digits[--i];
    -                }
    -                if ( q == '9' ){
    -                    // carryout! High-order 1, rest 0s, larger exp.
    -                    digits[0] = '1';
    -                    Arrays.fill(digits, 1, nDigits, '0');
    -                    return decExp+1;
    -                }
    -            }
    -            digits[i] = (char)(q + 1);
    -            Arrays.fill(digits, i+1, nDigits, '0');
    -        } else {
    -            Arrays.fill(digits, prec, nDigits, '0');
    -        }
    -        return decExp;
    -    }
    -
    -    /**
    -     * Fills mantissa and exponent char arrays for compatible format.
    -     */
    -    private void fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
    -        int startIndex = isNegative ? 1 : 0;
    -        if (exp > 0 && exp < 8) {
    -            // print digits.digits.
    -            if (nDigits < exp) {
    -                int extraZeros = exp - nDigits;
    -                mantissa = create(isNegative, nDigits + extraZeros + 2);
    -                System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
    -                Arrays.fill(mantissa, startIndex + nDigits, startIndex + nDigits + extraZeros, '0');
    -                mantissa[startIndex + nDigits + extraZeros] = '.';
    -                mantissa[startIndex + nDigits + extraZeros+1] = '0';
    -            } else if (exp < nDigits) {
    -                int t = Math.min(nDigits - exp, precision);
    -                mantissa = create(isNegative, exp + 1 + t);
    -                System.arraycopy(digits, 0, mantissa, startIndex, exp);
    -                mantissa[startIndex + exp ] = '.';
    -                System.arraycopy(digits, exp, mantissa, startIndex+exp+1, t);
    -            } else { // exp == digits.length
    -                mantissa = create(isNegative, nDigits + 2);
    -                System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
    -                mantissa[startIndex + nDigits ] = '.';
    -                mantissa[startIndex + nDigits +1] = '0';
    -            }
    -        } else if (exp <= 0 && exp > -3) {
    -            int zeros = Math.max(0, Math.min(-exp, precision));
    -            int t = Math.max(0, Math.min(nDigits, precision + exp));
    -            // write '0' s before the significant digits
    -            if (zeros > 0) {
    -                mantissa = create(isNegative, zeros + 2 + t);
    -                mantissa[startIndex] = '0';
    -                mantissa[startIndex+1] = '.';
    -                Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
    -                if (t > 0) {
    -                    // copy only when significant digits are within the precision
    -                    System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
    -                }
    -            } else if (t > 0) {
    -                mantissa = create(isNegative, zeros + 2 + t);
    -                mantissa[startIndex] = '0';
    -                mantissa[startIndex + 1] = '.';
    -                // copy only when significant digits are within the precision
    -                System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
    -            } else {
    -                this.mantissa = create(isNegative, 1);
    -                this.mantissa[startIndex] = '0';
    -            }
    -        } else {
    -            if (nDigits > 1) {
    -                mantissa = create(isNegative, nDigits + 1);
    -                mantissa[startIndex] = digits[0];
    -                mantissa[startIndex + 1] = '.';
    -                System.arraycopy(digits, 1, mantissa, startIndex + 2, nDigits - 1);
    -            } else {
    -                mantissa = create(isNegative, 3);
    -                mantissa[startIndex] = digits[0];
    -                mantissa[startIndex + 1] = '.';
    -                mantissa[startIndex + 2] = '0';
    -            }
    -            int e, expStartIntex;
    -            boolean isNegExp = (exp <= 0);
    -            if (isNegExp) {
    -                e = -exp + 1;
    -                expStartIntex = 1;
    -            } else {
    -                e = exp - 1;
    -                expStartIntex = 0;
    -            }
    -            // decExponent has 1, 2, or 3, digits
    -            if (e <= 9) {
    -                exponent = create(isNegExp,1);
    -                exponent[expStartIntex] = (char) (e + '0');
    -            } else if (e <= 99) {
    -                exponent = create(isNegExp,2);
    -                exponent[expStartIntex] = (char) (e / 10 + '0');
    -                exponent[expStartIntex+1] = (char) (e % 10 + '0');
    -            } else {
    -                exponent = create(isNegExp,3);
    -                exponent[expStartIntex] = (char) (e / 100 + '0');
    -                e %= 100;
    -                exponent[expStartIntex+1] = (char) (e / 10 + '0');
    -                exponent[expStartIntex+2] = (char) (e % 10 + '0');
    -            }
    -        }
    -    }
    -
    -    private static char[] create(boolean isNegative, int size) {
    -        if(isNegative) {
    -            char[] r = new char[size +1];
    -            r[0] = '-';
    -            return r;
    -        } else {
    -            return new char[size];
    -        }
    -    }
    -
    -    /*
    -     * Fills mantissa char arrays for DECIMAL_FLOAT format.
    -     * Exponent should be equal to null.
    -     */
    -    private void fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
    -        int startIndex = isNegative ? 1 : 0;
    -        if (exp > 0) {
    -            // print digits.digits.
    -            if (nDigits < exp) {
    -                mantissa = create(isNegative,exp);
    -                System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
    -                Arrays.fill(mantissa, startIndex + nDigits, startIndex + exp, '0');
    -                // Do not append ".0" for formatted floats since the user
    -                // may request that it be omitted. It is added as necessary
    -                // by the Formatter.
    -            } else {
    -                int t = Math.min(nDigits - exp, precision);
    -                mantissa = create(isNegative, exp + (t > 0 ? (t + 1) : 0));
    -                System.arraycopy(digits, 0, mantissa, startIndex, exp);
    -                // Do not append ".0" for formatted floats since the user
    -                // may request that it be omitted. It is added as necessary
    -                // by the Formatter.
    -                if (t > 0) {
    -                    mantissa[startIndex + exp] = '.';
    -                    System.arraycopy(digits, exp, mantissa, startIndex + exp + 1, t);
    -                }
    -            }
    -        } else if (exp <= 0) {
    -            int zeros = Math.max(0, Math.min(-exp, precision));
    -            int t = Math.max(0, Math.min(nDigits, precision + exp));
    -            // write '0' s before the significant digits
    -            if (zeros > 0) {
    -                mantissa = create(isNegative, zeros + 2 + t);
    -                mantissa[startIndex] = '0';
    -                mantissa[startIndex+1] = '.';
    -                Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
    -                if (t > 0) {
    -                    // copy only when significant digits are within the precision
    -                    System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
    -                }
    -            } else if (t > 0) {
    -                mantissa = create(isNegative, zeros + 2 + t);
    -                mantissa[startIndex] = '0';
    -                mantissa[startIndex + 1] = '.';
    -                // copy only when significant digits are within the precision
    -                System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
    -            } else {
    -                this.mantissa = create(isNegative, 1);
    -                this.mantissa[startIndex] = '0';
    -            }
    -        }
    -    }
    -
    -    /**
    -     * Fills mantissa and exponent char arrays for SCIENTIFIC format.
    -     */
    -    private void fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
    -        int startIndex = isNegative ? 1 : 0;
    -        int t = Math.max(0, Math.min(nDigits - 1, precision));
    -        if (t > 0) {
    -            mantissa = create(isNegative, t + 2);
    -            mantissa[startIndex] = digits[0];
    -            mantissa[startIndex + 1] = '.';
    -            System.arraycopy(digits, 1, mantissa, startIndex + 2, t);
    -        } else {
    -            mantissa = create(isNegative, 1);
    -            mantissa[startIndex] = digits[0];
    -        }
    -        char expSign;
    -        int e;
    -        if (exp <= 0) {
    -            expSign = '-';
    -            e = -exp + 1;
    -        } else {
    -            expSign = '+' ;
    -            e = exp - 1;
    -        }
    -        // decExponent has 1, 2, or 3, digits
    -        if (e <= 9) {
    -            exponent = new char[] { expSign,
    -                    '0', (char) (e + '0') };
    -        } else if (e <= 99) {
    -            exponent = new char[] { expSign,
    -                    (char) (e / 10 + '0'), (char) (e % 10 + '0') };
    -        } else {
    -            char hiExpChar = (char) (e / 100 + '0');
    -            e %= 100;
    -            exponent = new char[] { expSign,
    -                    hiExpChar, (char) (e / 10 + '0'), (char) (e % 10 + '0') };
    -        }
    -    }
    -}
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/misc/VM.java
    --- a/jdk/src/java.base/share/classes/sun/misc/VM.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/misc/VM.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -27,9 +27,6 @@
     
     import static java.lang.Thread.State.*;
     import java.util.Properties;
    -import java.util.HashMap;
    -import java.util.Map;
    -import java.util.Set;
     
     public class VM {
     
    @@ -288,10 +285,10 @@
         }
     
         /* Current count of objects pending for finalization */
    -    private static volatile int finalRefCount = 0;
    +    private static volatile int finalRefCount;
     
         /* Peak count of objects pending for finalization */
    -    private static volatile int peakFinalRefCount = 0;
    +    private static volatile int peakFinalRefCount;
     
         /*
          * Gets the number of objects pending for finalization.
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/net/www/http/HttpCapture.java
    --- a/jdk/src/java.base/share/classes/sun/net/www/http/HttpCapture.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/net/www/http/HttpCapture.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -54,12 +54,12 @@
      * @author jccollet
      */
     public class HttpCapture {
    -    private File file = null;
    +    private File file;
         private boolean incoming = true;
    -    private BufferedWriter out = null;
    -    private static boolean initialized = false;
    -    private static volatile ArrayList patterns = null;
    -    private static volatile ArrayList capFiles = null;
    +    private BufferedWriter out;
    +    private static boolean initialized;
    +    private static volatile ArrayList patterns;
    +    private static volatile ArrayList capFiles;
     
         private static synchronized void init() {
             initialized = true;
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java
    --- a/jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -98,7 +98,7 @@
         // from previous releases.
         private static boolean retryPostProp = true;
     
    -    volatile boolean keepingAlive = false;     /* this is a keep-alive connection */
    +    volatile boolean keepingAlive;    /* this is a keep-alive connection */
         int keepAliveConnections = -1;    /* number of keep-alives left */
     
         /**Idle timeout value, in milliseconds. Zero means infinity,
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -51,14 +51,14 @@
         protected final FileDescriptor fd;
     
         // the local address to which the channel's socket is bound
    -    protected volatile InetSocketAddress localAddress = null;
    +    protected volatile InetSocketAddress localAddress;
     
         // need this lock to set local address
         private final Object stateLock = new Object();
     
         // close support
         private ReadWriteLock closeLock = new ReentrantReadWriteLock();
    -    private volatile boolean open = true;
    +    private volatile boolean closed;
     
         // set true when accept operation is cancelled
         private volatile boolean acceptKilled;
    @@ -73,7 +73,7 @@
     
         @Override
         public final boolean isOpen() {
    -        return open;
    +        return !closed;
         }
     
         /**
    @@ -102,9 +102,9 @@
             // synchronize with any threads using file descriptor/handle
             closeLock.writeLock().lock();
             try {
    -            if (!open)
    +            if (closed)
                     return;     // already closed
    -            open = false;
    +            closed = true;
             } finally {
                 closeLock.writeLock().unlock();
             }
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -54,8 +54,8 @@
         // protects state, localAddress, and remoteAddress
         protected final Object stateLock = new Object();
     
    -    protected volatile InetSocketAddress localAddress = null;
    -    protected volatile InetSocketAddress remoteAddress = null;
    +    protected volatile InetSocketAddress localAddress;
    +    protected volatile InetSocketAddress remoteAddress;
     
         // State, increases monotonically
         static final int ST_UNINITIALIZED = -1;
    @@ -78,7 +78,7 @@
     
         // close support
         private final ReadWriteLock closeLock = new ReentrantReadWriteLock();
    -    private volatile boolean open = true;
    +    private volatile boolean closed;
     
         // set true when exclusive binding is on and SO_REUSEADDR is emulated
         private boolean isReuseAddress;
    @@ -106,7 +106,7 @@
     
         @Override
         public final boolean isOpen() {
    -        return open;
    +        return !closed;
         }
     
         /**
    @@ -135,9 +135,9 @@
             // synchronize with any threads initiating asynchronous operations
             closeLock.writeLock().lock();
             try {
    -            if (!open)
    +            if (closed)
                     return;     // already closed
    -            open = false;
    +            closed = true;
             } finally {
                 closeLock.writeLock().unlock();
             }
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -58,8 +58,8 @@
         private final ProtocolFamily family;
     
         // IDs of native threads doing reads and writes, for signalling
    -    private volatile long readerThread = 0;
    -    private volatile long writerThread = 0;
    +    private volatile long readerThread;
    +    private volatile long writerThread;
     
         // Cached InetAddress and port for unconnected DatagramChannels
         // used by receive0
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -46,7 +46,7 @@
         private final DatagramChannelImpl dc;
     
         // Timeout "option" value for receives
    -    private volatile int timeout = 0;
    +    private volatile int timeout;
     
         // ## super will create a useless impl
         private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException {
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/FileLockImpl.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/FileLockImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/FileLockImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -31,7 +31,7 @@
     public class FileLockImpl
         extends FileLock
     {
    -    private volatile boolean valid = true;
    +    private volatile boolean invalid;
     
         FileLockImpl(FileChannel channel, long position, long size, boolean shared)
         {
    @@ -44,25 +44,25 @@
         }
     
         public boolean isValid() {
    -        return valid;
    +        return !invalid;
         }
     
         void invalidate() {
             assert Thread.holdsLock(this);
    -        valid = false;
    +        invalid = true;
         }
     
         public synchronized void release() throws IOException {
             Channel ch = acquiredBy();
             if (!ch.isOpen())
                 throw new ClosedChannelException();
    -        if (valid) {
    +        if (isValid()) {
                 if (ch instanceof FileChannelImpl)
                     ((FileChannelImpl)ch).release(this);
                 else if (ch instanceof AsynchronousFileChannelImpl)
                     ((AsynchronousFileChannelImpl)ch).release(this);
                 else throw new AssertionError();
    -            valid = false;
    +            invalidate();
             }
         }
     }
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/MembershipKeyImpl.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/MembershipKeyImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/MembershipKeyImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -43,8 +43,7 @@
         private final NetworkInterface interf;
         private final InetAddress source;
     
    -    // true when key is valid
    -    private volatile boolean valid = true;
    +    private volatile boolean invalid;
     
         // lock used when creating or accessing blockedSet
         private Object stateLock = new Object();
    @@ -134,12 +133,12 @@
         }
     
         public boolean isValid() {
    -        return valid;
    +        return !invalid;
         }
     
         // package-private
         void invalidate() {
    -        valid = false;
    +        invalid = true;
         }
     
         public void drop() {
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/Net.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/Net.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/Net.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -32,7 +32,6 @@
     import java.util.*;
     import java.security.AccessController;
     import java.security.PrivilegedAction;
    -import java.security.PrivilegedExceptionAction;
     import sun.net.ExtendedOptionsImpl;
     
     
    @@ -55,7 +54,7 @@
     
         // -- Miscellaneous utilities --
     
    -    private static volatile boolean checkedIPv6 = false;
    +    private static volatile boolean checkedIPv6;
         private static volatile boolean isIPv6Available;
     
         /**
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketAdaptor.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketAdaptor.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketAdaptor.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -45,7 +45,7 @@
         private final ServerSocketChannelImpl ssc;
     
         // Timeout "option" value for accepts
    -    private volatile int timeout = 0;
    +    private volatile int timeout;
     
         public static ServerSocket create(ServerSocketChannelImpl ssc) {
             try {
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -54,7 +54,7 @@
         private int fdVal;
     
         // ID of native thread currently blocked in this channel, for signalling
    -    private volatile long thread = 0;
    +    private volatile long thread;
     
         // Lock held by thread currently blocked in this channel
         private final Object lock = new Object();
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -26,13 +26,11 @@
     package sun.nio.ch;
     
     import java.io.*;
    -import java.lang.ref.*;
     import java.net.*;
     import java.nio.*;
     import java.nio.channels.*;
     import java.security.AccessController;
     import java.security.PrivilegedExceptionAction;
    -import java.util.*;
     
     
     // Make a socket channel look like a socket.
    @@ -55,7 +53,7 @@
         private final SocketChannelImpl sc;
     
         // Timeout "option" value for reads
    -    private volatile int timeout = 0;
    +    private volatile int timeout;
     
         private SocketAdaptor(SocketChannelImpl sc) throws SocketException {
             super((SocketImpl) null);
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -56,8 +56,8 @@
         private final int fdVal;
     
         // IDs of native threads doing reads and writes, for signalling
    -    private volatile long readerThread = 0;
    -    private volatile long writerThread = 0;
    +    private volatile long readerThread;
    +    private volatile long writerThread;
     
         // Lock held by current reading or connecting thread
         private final Object readLock = new Object();
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/ch/Util.java
    --- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -25,13 +25,10 @@
     
     package sun.nio.ch;
     
    -import java.lang.ref.SoftReference;
     import java.lang.reflect.*;
    -import java.io.IOException;
     import java.io.FileDescriptor;
     import java.nio.ByteBuffer;
     import java.nio.MappedByteBuffer;
    -import java.nio.channels.*;
     import java.security.AccessController;
     import java.security.PrivilegedAction;
     import java.util.*;
    @@ -295,7 +292,7 @@
             return pageSize;
         }
     
    -    private static volatile Constructor directByteBufferConstructor = null;
    +    private static volatile Constructor directByteBufferConstructor;
     
         private static void initDBBConstructor() {
             AccessController.doPrivileged(new PrivilegedAction() {
    @@ -340,7 +337,7 @@
             return dbb;
         }
     
    -    private static volatile Constructor directByteBufferRConstructor = null;
    +    private static volatile Constructor directByteBufferRConstructor;
     
         private static void initDBBRConstructor() {
             AccessController.doPrivileged(new PrivilegedAction() {
    @@ -388,7 +385,7 @@
     
         // -- Bug compatibility --
     
    -    private static volatile String bugLevel = null;
    +    private static volatile String bugLevel;
     
         static boolean atBugLevel(String bl) {              // package-private
             if (bugLevel == null) {
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java
    --- a/jdk/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -39,10 +39,10 @@
         private static final int MIN_BYTE_BUFFER_SIZE = 32;
         private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
     
    -    private volatile boolean isOpen = true;
    +    private volatile boolean closed;
     
         private void ensureOpen() throws IOException {
    -        if (!isOpen)
    +        if (closed)
                 throw new IOException("Stream closed");
         }
     
    @@ -188,15 +188,15 @@
     
         public void close() throws IOException {
             synchronized (lock) {
    -            if (!isOpen)
    +            if (closed)
                     return;
                 implClose();
    -            isOpen = false;
    +            closed = true;
             }
         }
     
         private boolean isOpen() {
    -        return isOpen;
    +        return !closed;
         }
     
     
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java
    --- a/jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -38,10 +38,10 @@
     
         private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
     
    -    private volatile boolean isOpen = true;
    +    private volatile boolean closed;
     
         private void ensureOpen() throws IOException {
    -        if (!isOpen)
    +        if (closed)
                 throw new IOException("Stream closed");
         }
     
    @@ -156,15 +156,15 @@
     
         public void close() throws IOException {
             synchronized (lock) {
    -            if (!isOpen)
    +            if (closed)
                     return;
                 implClose();
    -            isOpen = false;
    +            closed = true;
             }
         }
     
         private boolean isOpen() {
    -        return isOpen;
    +        return !closed;
         }
     
     
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java
    --- a/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -44,9 +44,9 @@
         // Only used if forSerialization is true
         private static final short NUM_SERIALIZATION_CPOOL_ENTRIES = (short) 2;
     
    -    private static volatile int methodSymnum = 0;
    -    private static volatile int constructorSymnum = 0;
    -    private static volatile int serializationConstructorSymnum = 0;
    +    private static volatile int methodSymnum;
    +    private static volatile int constructorSymnum;
    +    private static volatile int serializationConstructorSymnum;
     
         private Class   declaringClass;
         private Class[] parameterTypes;
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java
    --- a/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -299,7 +299,7 @@
                     }});
         }
     
    -    private transient volatile Method[] memberMethods = null;
    +    private transient volatile Method[] memberMethods;
     
         /**
          * Validates that a method is structurally appropriate for an
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java
    --- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -1105,41 +1105,6 @@
             return fragLen;
         }
     
    -
    -    /*
    -     * Is this cipher available?
    -     *
    -     * This method can only be called by CipherSuite.BulkCipher.isAvailable()
    -     * to test the availability of a cipher suites.  Please DON'T use it in
    -     * other places, otherwise, the behavior may be unexpected because we may
    -     * initialize AEAD cipher improperly in the method.
    -     */
    -    Boolean isAvailable() {
    -        // We won't know whether a cipher for a particular key size is
    -        // available until the cipher is successfully initialized.
    -        //
    -        // We do not initialize AEAD cipher in the constructor.  Need to
    -        // initialize the cipher to ensure that the AEAD mode for a
    -        // particular key size is supported.
    -        if (cipherType == AEAD_CIPHER) {
    -            try {
    -                Authenticator authenticator =
    -                    new Authenticator(protocolVersion);
    -                byte[] nonce = authenticator.sequenceNumber();
    -                byte[] iv = Arrays.copyOf(fixedIv,
    -                                            fixedIv.length + nonce.length);
    -                System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length);
    -                GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv);
    -
    -                cipher.init(mode, key, spec, random);
    -            } catch (Exception e) {
    -                return Boolean.FALSE;
    -            }
    -        }   // Otherwise, we have initialized the cipher in the constructor.
    -
    -        return Boolean.TRUE;
    -    }
    -
         /**
          * Sanity check the length of a fragment before decryption.
          *
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java
    --- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -77,12 +77,6 @@
         // minimum priority for default enabled CipherSuites
         static final int DEFAULT_SUITES_PRIORITY = 300;
     
    -    // Flag indicating if CipherSuite availability can change dynamically.
    -    // This is the case when we rely on a JCE cipher implementation that
    -    // may not be available in the installed JCE providers.
    -    // It is true because we might not have an ECC implementation.
    -    static final boolean DYNAMIC_AVAILABILITY = true;
    -
         private static final boolean ALLOW_ECC = Debug.getBooleanProperty
             ("com.sun.net.ssl.enableECC", true);
     
    @@ -176,9 +170,6 @@
          * Return whether this CipherSuite is available for use. A
          * CipherSuite may be unavailable even if it is supported
          * (i.e. allowed == true) if the required JCE cipher is not installed.
    -     * In some configuration, this situation may change over time, call
    -     * CipherSuiteList.clearAvailableCache() before this method to obtain
    -     * the most current status.
          */
         boolean isAvailable() {
             return allowed && keyExchange.isAvailable() && cipher.isAvailable();
    @@ -471,10 +462,6 @@
             B_AES_128_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 16, 12, 4, true),
             B_AES_256_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 32, 12, 4, true);
     
    -        // Map BulkCipher -> Boolean(available)
    -        private static final Map availableCache =
    -                                            new HashMap<>(8);
    -
             // descriptive name including key size, e.g. AES/128
             final String description;
     
    @@ -518,6 +505,9 @@
             // The secure random used to detect the cipher availability.
             private static final SecureRandom secureRandom;
     
    +        // runtime availability
    +        private final boolean isAvailable;
    +
             static {
                 try {
                     secureRandom = JsseJce.getSecureRandom();
    @@ -542,6 +532,17 @@
     
                 this.expandedKeySize = expandedKeySize;
                 this.exportable = true;
    +
    +            // availability of this bulk cipher
    +            //
    +            // Currently all supported ciphers except AES are always available
    +            // via the JSSE internal implementations. We also assume AES/128 of
    +            // CBC mode is always available since it is shipped with the SunJCE
    +            // provider.  However, AES/256 is unavailable when the default JCE
    +            // policy jurisdiction files are installed because of key length
    +            // restrictions.
    +            this.isAvailable =
    +                    allowed ? isUnlimited(keySize, transformation) : false;
             }
     
             BulkCipher(String transformation, CipherType cipherType, int keySize,
    @@ -558,6 +559,17 @@
     
                 this.expandedKeySize = keySize;
                 this.exportable = false;
    +
    +            // availability of this bulk cipher
    +            //
    +            // Currently all supported ciphers except AES are always available
    +            // via the JSSE internal implementations. We also assume AES/128 of
    +            // CBC mode is always available since it is shipped with the SunJCE
    +            // provider.  However, AES/256 is unavailable when the default JCE
    +            // policy jurisdiction files are installed because of key length
    +            // restrictions.
    +            this.isAvailable =
    +                    allowed ? isUnlimited(keySize, transformation) : false;
             }
     
             /**
    @@ -575,84 +587,27 @@
     
             /**
              * Test if this bulk cipher is available. For use by CipherSuite.
    -         *
    -         * Currently all supported ciphers except AES are always available
    -         * via the JSSE internal implementations. We also assume AES/128 of
    -         * CBC mode is always available since it is shipped with the SunJCE
    -         * provider.  However, AES/256 is unavailable when the default JCE
    -         * policy jurisdiction files are installed because of key length
    -         * restrictions, and AEAD is unavailable when the underlying providers
    -         * do not support AEAD/GCM mode.
              */
             boolean isAvailable() {
    -            if (allowed == false) {
    -                return false;
    -            }
    -
    -            if ((this == B_AES_256) ||
    -                    (this.cipherType == CipherType.AEAD_CIPHER)) {
    -                return isAvailable(this);
    -            }
    -
    -            // always available
    -            return true;
    -        }
    -
    -        // for use by CipherSuiteList.clearAvailableCache();
    -        static synchronized void clearAvailableCache() {
    -            if (DYNAMIC_AVAILABILITY) {
    -                availableCache.clear();
    -            }
    +            return this.isAvailable;
             }
     
    -        private static synchronized boolean isAvailable(BulkCipher cipher) {
    -            Boolean b = availableCache.get(cipher);
    -            if (b == null) {
    -                int keySizeInBits = cipher.keySize * 8;
    -                if (keySizeInBits > 128) {    // need the JCE unlimited
    -                                               // strength jurisdiction policy
    -                    try {
    -                        if (Cipher.getMaxAllowedKeyLength(
    -                                cipher.transformation) < keySizeInBits) {
    -                            b = Boolean.FALSE;
    -                        }
    -                    } catch (Exception e) {
    -                        b = Boolean.FALSE;
    -                    }
    -                }
    +        private static boolean isUnlimited(int keySize, String transformation) {
    +            int keySizeInBits = keySize * 8;
    +            if (keySizeInBits > 128) {    // need the JCE unlimited
    +                                          // strength jurisdiction policy
    +                try {
    +                    if (Cipher.getMaxAllowedKeyLength(
    +                            transformation) < keySizeInBits) {
     
    -                if (b == null) {
    -                    b = Boolean.FALSE;          // may be reset to TRUE if
    -                                                // the cipher is available
    -                    CipherBox temporary = null;
    -                    try {
    -                        SecretKey key = new SecretKeySpec(
    -                                            new byte[cipher.expandedKeySize],
    -                                            cipher.algorithm);
    -                        IvParameterSpec iv;
    -                        if (cipher.cipherType == CipherType.AEAD_CIPHER) {
    -                            iv = new IvParameterSpec(
    -                                            new byte[cipher.fixedIvSize]);
    -                        } else {
    -                            iv = new IvParameterSpec(new byte[cipher.ivSize]);
    -                        }
    -                        temporary = cipher.newCipher(
    -                                            ProtocolVersion.DEFAULT_TLS,
    -                                            key, iv, secureRandom, true);
    -                        b = temporary.isAvailable();
    -                    } catch (NoSuchAlgorithmException e) {
    -                        // not available
    -                    } finally {
    -                        if (temporary != null) {
    -                            temporary.dispose();
    -                        }
    +                        return false;
                         }
    +                } catch (Exception e) {
    +                    return false;
                     }
    -
    -                availableCache.put(cipher, b);
                 }
     
    -            return b.booleanValue();
    +            return true;
             }
     
             @Override
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java
    --- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -74,24 +74,12 @@
                 throw new IllegalArgumentException("CipherSuites may not be null");
             }
             cipherSuites = new ArrayList(names.length);
    -        // refresh available cache once if a CipherSuite is not available
    -        // (maybe new JCE providers have been installed)
    -        boolean refreshed = false;
             for (int i = 0; i < names.length; i++) {
                 String suiteName = names[i];
                 CipherSuite suite = CipherSuite.valueOf(suiteName);
                 if (suite.isAvailable() == false) {
    -                if (refreshed == false) {
    -                    // clear the cache so that the isAvailable() call below
    -                    // does a full check
    -                    clearAvailableCache();
    -                    refreshed = true;
    -                }
    -                // still missing?
    -                if (suite.isAvailable() == false) {
    -                    throw new IllegalArgumentException("Cannot support "
    -                        + suiteName + " with currently installed providers");
    -                }
    +                throw new IllegalArgumentException("Cannot support "
    +                    + suiteName + " with currently installed providers");
                 }
                 cipherSuites.add(suite);
             }
    @@ -195,16 +183,4 @@
             }
             s.putBytes16(suiteBytes);
         }
    -
    -    /**
    -     * Clear cache of available ciphersuites. If we support all ciphers
    -     * internally, there is no need to clear the cache and calling this
    -     * method has no effect.
    -     */
    -    static synchronized void clearAvailableCache() {
    -        if (CipherSuite.DYNAMIC_AVAILABILITY) {
    -            CipherSuite.BulkCipher.clearAvailableCache();
    -            JsseJce.clearEcAvailable();
    -        }
    -    }
     }
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java
    --- a/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -55,11 +55,6 @@
     
         private static final ProviderList fipsProviderList;
     
    -    // Flag indicating whether EC crypto is available.
    -    // If null, then we have not checked yet.
    -    // If yes, then all the EC based crypto we need is available.
    -    private static Boolean ecAvailable;
    -
         // Flag indicating whether Kerberos crypto is available.
         // If true, then all the Kerberos-based crypto we need is available.
         private static final boolean kerberosAvailable;
    @@ -180,24 +175,8 @@
             // no instantiation of this class
         }
     
    -    static synchronized boolean isEcAvailable() {
    -        if (ecAvailable == null) {
    -            try {
    -                JsseJce.getSignature(SIGNATURE_ECDSA);
    -                JsseJce.getSignature(SIGNATURE_RAWECDSA);
    -                JsseJce.getKeyAgreement("ECDH");
    -                JsseJce.getKeyFactory("EC");
    -                JsseJce.getKeyPairGenerator("EC");
    -                ecAvailable = true;
    -            } catch (Exception e) {
    -                ecAvailable = false;
    -            }
    -        }
    -        return ecAvailable;
    -    }
    -
    -    static synchronized void clearEcAvailable() {
    -        ecAvailable = null;
    +    static boolean isEcAvailable() {
    +        return EcAvailability.isAvailable;
         }
     
         static boolean isKerberosAvailable() {
    @@ -399,4 +378,27 @@
             }
         }
     
    +
    +    // lazy initialization holder class idiom for static default parameters
    +    //
    +    // See Effective Java Second Edition: Item 71.
    +    private static class EcAvailability {
    +        // Is EC crypto available?
    +        private final static boolean isAvailable;
    +
    +        static {
    +            boolean mediator = true;
    +            try {
    +                JsseJce.getSignature(SIGNATURE_ECDSA);
    +                JsseJce.getSignature(SIGNATURE_RAWECDSA);
    +                JsseJce.getKeyAgreement("ECDH");
    +                JsseJce.getKeyFactory("EC");
    +                JsseJce.getKeyPairGenerator("EC");
    +            } catch (Exception e) {
    +                mediator = false;
    +            }
    +
    +            isAvailable = mediator;
    +        }
    +    }
     }
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java
    --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -52,18 +52,8 @@
         private X509TrustManager trustManager;
         private SecureRandom secureRandom;
     
    -    // supported and default protocols
    -    private ProtocolList defaultServerProtocolList;
    -    private ProtocolList defaultClientProtocolList;
    -    private ProtocolList supportedProtocolList;
    -
    -    // supported and default cipher suites
    -    private CipherSuiteList defaultServerCipherSuiteList;
    -    private CipherSuiteList defaultClientCipherSuiteList;
    -    private CipherSuiteList supportedCipherSuiteList;
    -
         // DTLS cookie exchange manager
    -    private HelloCookieManager helloCookieManager;
    +    private volatile HelloCookieManager helloCookieManager;
     
         private StatusResponseManager statusResponseManager;
     
    @@ -117,6 +107,7 @@
             if (debug != null && Debug.isOn("sslctx")) {
                 System.out.println("done seeding SecureRandom");
             }
    +
             isInitialized = true;
         }
     
    @@ -242,13 +233,20 @@
             return ephemeralKeyManager;
         }
     
    +    // Used for DTLS in server mode only, see ServerHandshaker.
         HelloCookieManager getHelloCookieManager() {
             if (!isInitialized) {
                 throw new IllegalStateException("SSLContext is not initialized");
             }
     
    -        if (helloCookieManager == null) {
    -            helloCookieManager = getHelloCookieManager(secureRandom);
    +        if (helloCookieManager != null) {
    +            return helloCookieManager;
    +        }
    +
    +        synchronized (this) {
    +            if (helloCookieManager == null) {
    +                helloCookieManager = getHelloCookieManager(secureRandom);
    +            }
             }
     
             return helloCookieManager;
    @@ -263,78 +261,34 @@
             return statusResponseManager;
         }
     
    -    abstract SSLParameters getDefaultServerSSLParams();
    -    abstract SSLParameters getDefaultClientSSLParams();
    -    abstract SSLParameters getSupportedSSLParams();
    +    // Get supported ProtocolList.
    +    abstract ProtocolList getSuportedProtocolList();
    +
    +    // Get default ProtocolList for server mode.
    +    abstract ProtocolList getServerDefaultProtocolList();
    +
    +    // Get default ProtocolList for client mode.
    +    abstract ProtocolList getClientDefaultProtocolList();
     
    -    // Get supported ProtocolList.
    -    ProtocolList getSuportedProtocolList() {
    -        if (supportedProtocolList == null) {
    -            supportedProtocolList =
    -                new ProtocolList(getSupportedSSLParams().getProtocols());
    -        }
    +    // Get supported CipherSuiteList.
    +    abstract CipherSuiteList getSupportedCipherSuiteList();
     
    -        return supportedProtocolList;
    -    }
    +    // Get default CipherSuiteList for server mode.
    +    abstract CipherSuiteList getServerDefaultCipherSuiteList();
    +
    +    // Get default CipherSuiteList for client mode.
    +    abstract CipherSuiteList getClientDefaultCipherSuiteList();
     
         // Get default ProtocolList.
         ProtocolList getDefaultProtocolList(boolean roleIsServer) {
    -        if (roleIsServer) {
    -            if (defaultServerProtocolList == null) {
    -                defaultServerProtocolList = new ProtocolList(
    -                        getDefaultServerSSLParams().getProtocols());
    -            }
    -
    -            return defaultServerProtocolList;
    -        } else {
    -            if (defaultClientProtocolList == null) {
    -                defaultClientProtocolList = new ProtocolList(
    -                        getDefaultClientSSLParams().getProtocols());
    -            }
    -
    -            return defaultClientProtocolList;
    -        }
    -    }
    -
    -    // Get supported CipherSuiteList.
    -    CipherSuiteList getSupportedCipherSuiteList() {
    -        // The maintenance of cipher suites needs to be synchronized.
    -        synchronized (this) {
    -            // Clear cache of available ciphersuites.
    -            clearAvailableCache();
    -
    -            if (supportedCipherSuiteList == null) {
    -                supportedCipherSuiteList = getApplicableCipherSuiteList(
    -                        getSuportedProtocolList(), false);
    -            }
    -
    -            return supportedCipherSuiteList;
    -        }
    +        return roleIsServer ? getServerDefaultProtocolList()
    +                            : getClientDefaultProtocolList();
         }
     
         // Get default CipherSuiteList.
         CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) {
    -        // The maintenance of cipher suites needs to be synchronized.
    -        synchronized (this) {
    -            // Clear cache of available ciphersuites.
    -            clearAvailableCache();
    -
    -            if (roleIsServer) {
    -                if (defaultServerCipherSuiteList == null) {
    -                    defaultServerCipherSuiteList = getApplicableCipherSuiteList(
    -                        getDefaultProtocolList(true), true);
    -                }
    -
    -                return defaultServerCipherSuiteList;
    -            } else {
    -                if (defaultClientCipherSuiteList == null) {
    -                    defaultClientCipherSuiteList = getApplicableCipherSuiteList(
    -                        getDefaultProtocolList(false), true);
    -                }
    -
    -                return defaultClientCipherSuiteList;
    -            }
    -        }
    +        return roleIsServer ? getServerDefaultCipherSuiteList()
    +                            : getClientDefaultCipherSuiteList();
         }
     
         /**
    @@ -342,8 +296,8 @@
          * protocols.  See: SSLSocket/SSLEngine.setEnabledProtocols()
          */
         boolean isDefaultProtocolList(ProtocolList protocols) {
    -        return (protocols == defaultServerProtocolList) ||
    -               (protocols == defaultClientProtocolList);
    +        return (protocols == getServerDefaultProtocolList()) ||
    +               (protocols == getClientDefaultProtocolList());
         }
     
         /**
    @@ -351,8 +305,8 @@
          * protocols.  See: SSLSocket/SSLEngine.setEnabledProtocols()
          */
         boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) {
    -        return (cipherSuites == defaultClientCipherSuiteList) ||
    -               (cipherSuites == defaultServerCipherSuiteList);
    +        return (cipherSuites == getServerDefaultCipherSuiteList()) ||
    +               (cipherSuites == getClientDefaultCipherSuiteList());
         }
     
         /*
    @@ -405,24 +359,6 @@
             return new CipherSuiteList(suites);
         }
     
    -    /**
    -     * Clear cache of available ciphersuites. If we support all ciphers
    -     * internally, there is no need to clear the cache and calling this
    -     * method has no effect.
    -     *
    -     * Note that every call to clearAvailableCache() and the maintenance of
    -     * cipher suites need to be synchronized with this instance.
    -     */
    -    private void clearAvailableCache() {
    -        if (CipherSuite.DYNAMIC_AVAILABILITY) {
    -            supportedCipherSuiteList = null;
    -            defaultServerCipherSuiteList = null;
    -            defaultClientCipherSuiteList = null;
    -            CipherSuite.BulkCipher.clearAvailableCache();
    -            JsseJce.clearEcAvailable();
    -        }
    -    }
    -
         private static String[] getAvailableProtocols(
                 ProtocolVersion[] protocolCandidates) {
     
    @@ -479,31 +415,28 @@
          * @see SSLContext
          */
         private abstract static class AbstractTLSContext extends SSLContextImpl {
    -        // parameters
    -        private static final SSLParameters defaultServerSSLParams;
    -        private static final SSLParameters supportedSSLParams;
    +        private static final ProtocolList supportedProtocolList;
    +        private static final ProtocolList serverDefaultProtocolList;
    +
    +        private static final CipherSuiteList supportedCipherSuiteList;
    +        private static final CipherSuiteList serverDefaultCipherSuiteList;
     
             static {
    -            // supported SSL parameters
    -            supportedSSLParams = new SSLParameters();
    -
    -            // candidates for available protocols
    -            ProtocolVersion[] candidates;
    -
                 if (SunJSSE.isFIPS()) {
    -                supportedSSLParams.setProtocols(new String[] {
    +                supportedProtocolList = new ProtocolList(new String[] {
                         ProtocolVersion.TLS10.name,
                         ProtocolVersion.TLS11.name,
                         ProtocolVersion.TLS12.name
                     });
     
    -                candidates = new ProtocolVersion[] {
    +                serverDefaultProtocolList = new ProtocolList(
    +                        getAvailableProtocols(new ProtocolVersion[] {
                         ProtocolVersion.TLS10,
                         ProtocolVersion.TLS11,
                         ProtocolVersion.TLS12
    -                };
    +                }));
                 } else {
    -                supportedSSLParams.setProtocols(new String[] {
    +                supportedProtocolList = new ProtocolList(new String[] {
                         ProtocolVersion.SSL20Hello.name,
                         ProtocolVersion.SSL30.name,
                         ProtocolVersion.TLS10.name,
    @@ -511,28 +444,40 @@
                         ProtocolVersion.TLS12.name
                     });
     
    -                candidates = new ProtocolVersion[] {
    +                serverDefaultProtocolList = new ProtocolList(
    +                        getAvailableProtocols(new ProtocolVersion[] {
                         ProtocolVersion.SSL20Hello,
                         ProtocolVersion.SSL30,
                         ProtocolVersion.TLS10,
                         ProtocolVersion.TLS11,
                         ProtocolVersion.TLS12
    -                };
    +                }));
                 }
     
    -            defaultServerSSLParams = new SSLParameters();
    -            defaultServerSSLParams.setProtocols(
    -                    getAvailableProtocols(candidates));
    +            supportedCipherSuiteList = getApplicableCipherSuiteList(
    +                    supportedProtocolList, false);          // all supported
    +            serverDefaultCipherSuiteList = getApplicableCipherSuiteList(
    +                    serverDefaultProtocolList, true);       // enabled only
             }
     
             @Override
    -        SSLParameters getDefaultServerSSLParams() {
    -            return defaultServerSSLParams;
    +        ProtocolList getSuportedProtocolList() {
    +            return supportedProtocolList;
             }
     
             @Override
    -        SSLParameters getSupportedSSLParams() {
    -            return supportedSSLParams;
    +        CipherSuiteList getSupportedCipherSuiteList() {
    +            return supportedCipherSuiteList;
    +        }
    +
    +        @Override
    +        ProtocolList getServerDefaultProtocolList() {
    +            return serverDefaultProtocolList;
    +        }
    +
    +        @Override
    +        CipherSuiteList getServerDefaultCipherSuiteList() {
    +            return serverDefaultCipherSuiteList;
             }
     
             @Override
    @@ -552,30 +497,35 @@
          * @see SSLContext
          */
         public static final class TLS10Context extends AbstractTLSContext {
    -        private static final SSLParameters defaultClientSSLParams;
    +        private static final ProtocolList clientDefaultProtocolList;
    +        private static final CipherSuiteList clientDefaultCipherSuiteList;
     
             static {
    -            // candidates for available protocols
    -            ProtocolVersion[] candidates;
                 if (SunJSSE.isFIPS()) {
    -                candidates = new ProtocolVersion[] {
    +                clientDefaultProtocolList = new ProtocolList(
    +                        getAvailableProtocols(new ProtocolVersion[] {
                         ProtocolVersion.TLS10
    -                };
    +                }));
                 } else {
    -                candidates = new ProtocolVersion[] {
    +                clientDefaultProtocolList = new ProtocolList(
    +                        getAvailableProtocols(new ProtocolVersion[] {
                         ProtocolVersion.SSL30,
                         ProtocolVersion.TLS10
    -                };
    +                }));
                 }
     
    -            defaultClientSSLParams = new SSLParameters();
    -            defaultClientSSLParams.setProtocols(
    -                    getAvailableProtocols(candidates));
    +            clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
    +                    clientDefaultProtocolList, true);       // enabled only
             }
     
             @Override
    -        SSLParameters getDefaultClientSSLParams() {
    -            return defaultClientSSLParams;
    +        ProtocolList getClientDefaultProtocolList() {
    +            return clientDefaultProtocolList;
    +        }
    +
    +        @Override
    +        CipherSuiteList getClientDefaultCipherSuiteList() {
    +            return clientDefaultCipherSuiteList;
             }
         }
     
    @@ -585,32 +535,37 @@
          * @see SSLContext
          */
         public static final class TLS11Context extends AbstractTLSContext {
    -        private static final SSLParameters defaultClientSSLParams;
    +        private static final ProtocolList clientDefaultProtocolList;
    +        private static final CipherSuiteList clientDefaultCipherSuiteList;
     
             static {
    -            // candidates for available protocols
    -            ProtocolVersion[] candidates;
                 if (SunJSSE.isFIPS()) {
    -                candidates = new ProtocolVersion[] {
    +                clientDefaultProtocolList = new ProtocolList(
    +                        getAvailableProtocols(new ProtocolVersion[] {
                         ProtocolVersion.TLS10,
                         ProtocolVersion.TLS11
    -                };
    +                }));
                 } else {
    -                candidates = new ProtocolVersion[] {
    +                clientDefaultProtocolList = new ProtocolList(
    +                        getAvailableProtocols(new ProtocolVersion[] {
                         ProtocolVersion.SSL30,
                         ProtocolVersion.TLS10,
                         ProtocolVersion.TLS11
    -                };
    +                }));
                 }
     
    -            defaultClientSSLParams = new SSLParameters();
    -            defaultClientSSLParams.setProtocols(
    -                    getAvailableProtocols(candidates));
    +            clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
    +                    clientDefaultProtocolList, true);       // enabled only
             }
     
             @Override
    -        SSLParameters getDefaultClientSSLParams() {
    -            return defaultClientSSLParams;
    +        ProtocolList getClientDefaultProtocolList() {
    +            return clientDefaultProtocolList;
    +        }
    +
    +        @Override
    +        CipherSuiteList getClientDefaultCipherSuiteList() {
    +            return clientDefaultCipherSuiteList;
             }
         }
     
    @@ -620,34 +575,39 @@
          * @see SSLContext
          */
         public static final class TLS12Context extends AbstractTLSContext {
    -        private static final SSLParameters defaultClientSSLParams;
    +        private static final ProtocolList clientDefaultProtocolList;
    +        private static final CipherSuiteList clientDefaultCipherSuiteList;
     
             static {
    -            // candidates for available protocols
    -            ProtocolVersion[] candidates;
                 if (SunJSSE.isFIPS()) {
    -                candidates = new ProtocolVersion[] {
    +                clientDefaultProtocolList = new ProtocolList(
    +                        getAvailableProtocols(new ProtocolVersion[] {
                         ProtocolVersion.TLS10,
                         ProtocolVersion.TLS11,
                         ProtocolVersion.TLS12
    -                };
    +                }));
                 } else {
    -                candidates = new ProtocolVersion[] {
    +                clientDefaultProtocolList = new ProtocolList(
    +                        getAvailableProtocols(new ProtocolVersion[] {
                         ProtocolVersion.SSL30,
                         ProtocolVersion.TLS10,
                         ProtocolVersion.TLS11,
                         ProtocolVersion.TLS12
    -                };
    +                }));
                 }
     
    -            defaultClientSSLParams = new SSLParameters();
    -            defaultClientSSLParams.setProtocols(
    -                    getAvailableProtocols(candidates));
    +            clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
    +                    clientDefaultProtocolList, true);       // enabled only
             }
     
             @Override
    -        SSLParameters getDefaultClientSSLParams() {
    -            return defaultClientSSLParams;
    +        ProtocolList getClientDefaultProtocolList() {
    +            return clientDefaultProtocolList;
    +        }
    +
    +        @Override
    +        CipherSuiteList getClientDefaultCipherSuiteList() {
    +            return clientDefaultCipherSuiteList;
             }
         }
     
    @@ -719,7 +679,9 @@
          */
         private static class CustomizedTLSContext extends AbstractTLSContext {
     
    -        private static final SSLParameters defaultClientSSLParams;
    +        private static final ProtocolList clientDefaultProtocolList;
    +        private static final CipherSuiteList clientDefaultCipherSuiteList;
    +
             private static IllegalArgumentException reservedException = null;
     
             // Don't want a java.lang.LinkageError for illegal system property.
    @@ -766,11 +728,13 @@
                         candidates = customizedTLSProtocols.toArray(candidates);
                     }
     
    -                defaultClientSSLParams = new SSLParameters();
    -                defaultClientSSLParams.setProtocols(
    +                clientDefaultProtocolList = new ProtocolList(
                             getAvailableProtocols(candidates));
    +                clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
    +                    clientDefaultProtocolList, true);   // enabled only
                 } else {
    -                defaultClientSSLParams = null;  // unlikely to be used
    +                clientDefaultProtocolList = null;       // unlikely to be used
    +                clientDefaultCipherSuiteList = null;    // unlikely to be used
                 }
             }
     
    @@ -781,8 +745,13 @@
             }
     
             @Override
    -        SSLParameters getDefaultClientSSLParams() {
    -            return defaultClientSSLParams;
    +        ProtocolList getClientDefaultProtocolList() {
    +            return clientDefaultProtocolList;
    +        }
    +
    +        @Override
    +        CipherSuiteList getClientDefaultCipherSuiteList() {
    +            return clientDefaultCipherSuiteList;
             }
         }
     
    @@ -795,71 +764,53 @@
             // use the default constructor and methods
         }
     
    -    /*
    -     * The SSLContext implementation for default "Default" algorithm
    -     *
    -     * @see SSLContext
    -     */
    -    public static final class DefaultSSLContext extends CustomizedTLSContext {
    +    // lazy initialization holder class idiom for static default parameters
    +    //
    +    // See Effective Java Second Edition: Item 71.
    +    private static final class DefaultManagersHolder {
             private static final String NONE = "NONE";
             private static final String P11KEYSTORE = "PKCS11";
     
    -        private static volatile SSLContextImpl defaultImpl;
    +        private static final TrustManager[] trustManagers;
    +        private static final KeyManager[] keyManagers;
     
    -        private static TrustManager[] defaultTrustManagers;
    -        private static KeyManager[] defaultKeyManagers;
    +        static Exception reservedException = null;
     
    -        public DefaultSSLContext() throws Exception {
    +        static {
    +            TrustManager[] tmMediator;
                 try {
    -                super.engineInit(getDefaultKeyManager(),
    -                        getDefaultTrustManager(), null);
    +                tmMediator = getTrustManagers();
                 } catch (Exception e) {
    -                if (debug != null && Debug.isOn("defaultctx")) {
    -                    System.out.println("default context init failed: " + e);
    +                reservedException = e;
    +                tmMediator = new TrustManager[0];
    +            }
    +            trustManagers = tmMediator;
    +
    +            if (reservedException == null) {
    +                KeyManager[] kmMediator;
    +                try {
    +                    kmMediator = getKeyManagers();
    +                } catch (Exception e) {
    +                    reservedException = e;
    +                    kmMediator = new KeyManager[0];
                     }
    -                throw e;
    -            }
    -
    -            if (defaultImpl == null) {
    -                defaultImpl = this;
    +                keyManagers = kmMediator;
    +            } else {
    +                keyManagers = new KeyManager[0];
                 }
             }
     
    -        @Override
    -        protected void engineInit(KeyManager[] km, TrustManager[] tm,
    -            SecureRandom sr) throws KeyManagementException {
    -            throw new KeyManagementException
    -                ("Default SSLContext is initialized automatically");
    -        }
    -
    -        static synchronized SSLContextImpl getDefaultImpl() throws Exception {
    -            if (defaultImpl == null) {
    -                new DefaultSSLContext();
    -            }
    -            return defaultImpl;
    -        }
    -
    -        private static synchronized TrustManager[] getDefaultTrustManager()
    -                throws Exception {
    -            if (defaultTrustManagers != null) {
    -                return defaultTrustManagers;
    -            }
    -
    +        private static TrustManager[] getTrustManagers() throws Exception {
                 KeyStore ks =
                     TrustManagerFactoryImpl.getCacertsKeyStore("defaultctx");
     
                 TrustManagerFactory tmf = TrustManagerFactory.getInstance(
                     TrustManagerFactory.getDefaultAlgorithm());
                 tmf.init(ks);
    -            defaultTrustManagers = tmf.getTrustManagers();
    -            return defaultTrustManagers;
    +            return tmf.getTrustManagers();
             }
     
    -        private static synchronized KeyManager[] getDefaultKeyManager()
    -                throws Exception {
    -            if (defaultKeyManagers != null) {
    -                return defaultKeyManagers;
    -            }
    +        private static KeyManager[] getKeyManagers() throws Exception {
     
                 final Map props = new HashMap<>();
                 AccessController.doPrivileged(
    @@ -956,8 +907,71 @@
                     kmf.init(ks, passwd);
                 }
     
    -            defaultKeyManagers = kmf.getKeyManagers();
    -            return defaultKeyManagers;
    +            return kmf.getKeyManagers();
    +        }
    +    }
    +
    +    // lazy initialization holder class idiom for static default parameters
    +    //
    +    // See Effective Java Second Edition: Item 71.
    +    private static final class DefaultSSLContextHolder {
    +
    +        private static final SSLContextImpl sslContext;
    +        static Exception reservedException = null;
    +
    +        static {
    +            SSLContextImpl mediator = null;
    +            if (DefaultManagersHolder.reservedException != null) {
    +                reservedException = DefaultManagersHolder.reservedException;
    +            } else {
    +                try {
    +                    mediator = new DefaultSSLContext();
    +                } catch (Exception e) {
    +                    reservedException = e;
    +                }
    +            }
    +
    +            sslContext = mediator;
    +        }
    +    }
    +
    +    /*
    +     * The SSLContext implementation for default "Default" algorithm
    +     *
    +     * @see SSLContext
    +     */
    +    public static final class DefaultSSLContext extends CustomizedTLSContext {
    +
    +        // public constructor for SSLContext.getInstance("Default")
    +        public DefaultSSLContext() throws Exception {
    +            if (DefaultManagersHolder.reservedException != null) {
    +                throw DefaultManagersHolder.reservedException;
    +            }
    +
    +            try {
    +                super.engineInit(DefaultManagersHolder.keyManagers,
    +                        DefaultManagersHolder.trustManagers, null);
    +            } catch (Exception e) {
    +                if (debug != null && Debug.isOn("defaultctx")) {
    +                    System.out.println("default context init failed: " + e);
    +                }
    +                throw e;
    +            }
    +        }
    +
    +        @Override
    +        protected void engineInit(KeyManager[] km, TrustManager[] tm,
    +            SecureRandom sr) throws KeyManagementException {
    +            throw new KeyManagementException
    +                ("Default SSLContext is initialized automatically");
    +        }
    +
    +        static SSLContextImpl getDefaultImpl() throws Exception {
    +            if (DefaultSSLContextHolder.reservedException != null) {
    +                throw DefaultSSLContextHolder.reservedException;
    +            }
    +
    +            return DefaultSSLContextHolder.sslContext;
             }
         }
     
    @@ -971,39 +985,50 @@
          * @see SSLContext
          */
         private abstract static class AbstractDTLSContext extends SSLContextImpl {
    -        // parameters
    -        private static final SSLParameters defaultServerSSLParams;
    -        private static final SSLParameters supportedSSLParams;
    +        private static final ProtocolList supportedProtocolList;
    +        private static final ProtocolList serverDefaultProtocolList;
    +
    +        private static final CipherSuiteList supportedCipherSuiteList;
    +        private static final CipherSuiteList serverDefaultCipherSuiteList;
     
             static {
    -            // supported SSL parameters
    -            supportedSSLParams = new SSLParameters();
    -
                 // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
    -            supportedSSLParams.setProtocols(new String[] {
    +            supportedProtocolList = new ProtocolList(new String[] {
                     ProtocolVersion.DTLS10.name,
                     ProtocolVersion.DTLS12.name
                 });
     
    -            // candidates for available protocols
    -            ProtocolVersion[] candidates = new ProtocolVersion[] {
    +            // available protocols for server mode
    +            serverDefaultProtocolList = new ProtocolList(
    +                    getAvailableProtocols(new ProtocolVersion[] {
                     ProtocolVersion.DTLS10,
                     ProtocolVersion.DTLS12
    -            };
    +            }));
     
    -            defaultServerSSLParams = new SSLParameters();
    -            defaultServerSSLParams.setProtocols(
    -                    getAvailableProtocols(candidates));
    +            supportedCipherSuiteList = getApplicableCipherSuiteList(
    +                    supportedProtocolList, false);          // all supported
    +            serverDefaultCipherSuiteList = getApplicableCipherSuiteList(
    +                    serverDefaultProtocolList, true);       // enabled only
             }
     
             @Override
    -        SSLParameters getDefaultServerSSLParams() {
    -            return defaultServerSSLParams;
    +        ProtocolList getSuportedProtocolList() {
    +            return supportedProtocolList;
             }
     
             @Override
    -        SSLParameters getSupportedSSLParams() {
    -            return supportedSSLParams;
    +        CipherSuiteList getSupportedCipherSuiteList() {
    +            return supportedCipherSuiteList;
    +        }
    +
    +        @Override
    +        ProtocolList getServerDefaultProtocolList() {
    +            return serverDefaultProtocolList;
    +        }
    +
    +        @Override
    +        CipherSuiteList getServerDefaultCipherSuiteList() {
    +            return serverDefaultCipherSuiteList;
             }
     
             @Override
    @@ -1028,22 +1053,28 @@
          * @see SSLContext
          */
         public static final class DTLS10Context extends AbstractDTLSContext {
    -        private static final SSLParameters defaultClientSSLParams;
    +        private static final ProtocolList clientDefaultProtocolList;
    +        private static final CipherSuiteList clientDefaultCipherSuiteList;
     
             static {
    -            // candidates for available protocols
    -            ProtocolVersion[] candidates = new ProtocolVersion[] {
    +            // available protocols for client mode
    +            clientDefaultProtocolList = new ProtocolList(
    +                    getAvailableProtocols(new ProtocolVersion[] {
                     ProtocolVersion.DTLS10
    -            };
    +            }));
     
    -            defaultClientSSLParams = new SSLParameters();
    -            defaultClientSSLParams.setProtocols(
    -                    getAvailableProtocols(candidates));
    +            clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
    +                    clientDefaultProtocolList, true);       // enabled only
             }
     
             @Override
    -        SSLParameters getDefaultClientSSLParams() {
    -            return defaultClientSSLParams;
    +        ProtocolList getClientDefaultProtocolList() {
    +            return clientDefaultProtocolList;
    +        }
    +
    +        @Override
    +        CipherSuiteList getClientDefaultCipherSuiteList() {
    +            return clientDefaultCipherSuiteList;
             }
         }
     
    @@ -1053,23 +1084,29 @@
          * @see SSLContext
          */
         public static final class DTLS12Context extends AbstractDTLSContext {
    -        private static final SSLParameters defaultClientSSLParams;
    +        private static final ProtocolList clientDefaultProtocolList;
    +        private static final CipherSuiteList clientDefaultCipherSuiteList;
     
             static {
    -            // candidates for available protocols
    -            ProtocolVersion[] candidates = new ProtocolVersion[] {
    +            // available protocols for client mode
    +            clientDefaultProtocolList = new ProtocolList(
    +                    getAvailableProtocols(new ProtocolVersion[] {
                     ProtocolVersion.DTLS10,
                     ProtocolVersion.DTLS12
    -            };
    +            }));
     
    -            defaultClientSSLParams = new SSLParameters();
    -            defaultClientSSLParams.setProtocols(
    -                    getAvailableProtocols(candidates));
    +            clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
    +                    clientDefaultProtocolList, true);       // enabled only
             }
     
             @Override
    -        SSLParameters getDefaultClientSSLParams() {
    -            return defaultClientSSLParams;
    +        ProtocolList getClientDefaultProtocolList() {
    +            return clientDefaultProtocolList;
    +        }
    +
    +        @Override
    +        CipherSuiteList getClientDefaultCipherSuiteList() {
    +            return clientDefaultCipherSuiteList;
             }
         }
     
    @@ -1079,7 +1116,9 @@
          * @see SSLContext
          */
         private static class CustomizedDTLSContext extends AbstractDTLSContext {
    -        private static final SSLParameters defaultClientSSLParams;
    +        private static final ProtocolList clientDefaultProtocolList;
    +        private static final CipherSuiteList clientDefaultCipherSuiteList;
    +
             private static IllegalArgumentException reservedException = null;
     
             // Don't want a java.lang.LinkageError for illegal system property.
    @@ -1119,11 +1158,13 @@
                         candidates = customizedDTLSProtocols.toArray(candidates);
                     }
     
    -                defaultClientSSLParams = new SSLParameters();
    -                defaultClientSSLParams.setProtocols(
    +                clientDefaultProtocolList = new ProtocolList(
                             getAvailableProtocols(candidates));
    +                clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
    +                    clientDefaultProtocolList, true);   // enabled only
                 } else {
    -                defaultClientSSLParams = null;   // unlikely to be used
    +                clientDefaultProtocolList = null;       // unlikely to be used
    +                clientDefaultCipherSuiteList = null;    // unlikely to be used
                 }
             }
     
    @@ -1134,8 +1175,13 @@
             }
     
             @Override
    -        SSLParameters getDefaultClientSSLParams() {
    -            return defaultClientSSLParams;
    +        ProtocolList getClientDefaultProtocolList() {
    +            return clientDefaultProtocolList;
    +        }
    +
    +        @Override
    +        CipherSuiteList getClientDefaultCipherSuiteList() {
    +            return clientDefaultCipherSuiteList;
             }
         }
     
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java
    --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -130,7 +130,7 @@
          * also since counters make shorter debugging IDs than the big ones
          * we use in the protocol for uniqueness-over-time.
          */
    -    private static volatile int counter = 0;
    +    private static volatile int counter;
     
         /*
          * Use of session caches is globally enabled/disabled.
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java
    --- a/jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -1290,7 +1290,7 @@
                 implements Comparable {
             final X500Principal issuer;
             final BigInteger serial;
    -        volatile int hashcode = 0;
    +        volatile int hashcode;
     
             /**
              * Create an X509IssuerSerial.
    @@ -1358,13 +1358,16 @@
              * @return the hash code value
              */
             public int hashCode() {
    -            if (hashcode == 0) {
    -                int result = 17;
    -                result = 37*result + issuer.hashCode();
    -                result = 37*result + serial.hashCode();
    -                hashcode = result;
    +            int h = hashcode;
    +            if (h == 0) {
    +                h = 17;
    +                h = 37*h + issuer.hashCode();
    +                h = 37*h + serial.hashCode();
    +                if (h != 0) {
    +                    hashcode = h;
    +                }
                 }
    -            return hashcode;
    +            return h;
             }
     
             @Override
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java
    --- a/jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -25,13 +25,6 @@
     
     package sun.util.calendar;
     
    -import java.io.File;
    -import java.io.FileInputStream;
    -import java.io.IOException;
    -import java.security.AccessController;
    -import java.security.PrivilegedActionException;
    -import java.security.PrivilegedExceptionAction;
    -import java.util.Properties;
     import java.util.TimeZone;
     import java.util.concurrent.ConcurrentHashMap;
     import java.util.concurrent.ConcurrentMap;
    @@ -76,7 +69,7 @@
     
         /////////////////////// Calendar Factory Methods /////////////////////////
     
    -    private static volatile boolean initialized = false;
    +    private static volatile boolean initialized;
     
         // Map of calendar names and calendar class names
         private static ConcurrentMap names;
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/util/locale/BaseLocale.java
    --- a/jdk/src/java.base/share/classes/sun/util/locale/BaseLocale.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/util/locale/BaseLocale.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -46,7 +46,7 @@
         private final String region;
         private final String variant;
     
    -    private volatile int hash = 0;
    +    private volatile int hash;
     
         // This method must be called only when creating the Locale.* constants.
         private BaseLocale(String language, String region) {
    @@ -147,7 +147,9 @@
                 h = 31 * h + script.hashCode();
                 h = 31 * h + region.hashCode();
                 h = 31 * h + variant.hashCode();
    -            hash = h;
    +            if (h != 0) {
    +                hash = h;
    +            }
             }
             return h;
         }
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java
    --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -114,20 +114,20 @@
             }
         }
     
    -    private volatile BreakIteratorProvider breakIteratorProvider = null;
    -    private volatile CollatorProvider collatorProvider = null;
    -    private volatile DateFormatProvider dateFormatProvider = null;
    -    private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider = null;
    -    private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider = null;
    -    private volatile NumberFormatProvider numberFormatProvider = null;
    +    private volatile BreakIteratorProvider breakIteratorProvider;
    +    private volatile CollatorProvider collatorProvider;
    +    private volatile DateFormatProvider dateFormatProvider;
    +    private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider;
    +    private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider;
    +    private volatile NumberFormatProvider numberFormatProvider;
     
    -    private volatile CurrencyNameProvider currencyNameProvider = null;
    -    private volatile LocaleNameProvider localeNameProvider = null;
    -    private volatile TimeZoneNameProvider timeZoneNameProvider = null;
    -    private volatile CalendarDataProvider calendarDataProvider = null;
    -    private volatile CalendarNameProvider calendarNameProvider = null;
    +    private volatile CurrencyNameProvider currencyNameProvider;
    +    private volatile LocaleNameProvider localeNameProvider;
    +    private volatile TimeZoneNameProvider timeZoneNameProvider;
    +    private volatile CalendarDataProvider calendarDataProvider;
    +    private volatile CalendarNameProvider calendarNameProvider;
     
    -    private volatile CalendarProvider calendarProvider = null;
    +    private volatile CalendarProvider calendarProvider;
     
         /*
          * Getter methods for java.text.spi.* providers
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java
    --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -107,7 +107,7 @@
          * Default fallback adapter type, which should return something meaningful in any case.
          * This is either CLDR or FALLBACK.
          */
    -    static volatile LocaleProviderAdapter.Type defaultLocaleProviderAdapter = null;
    +    static volatile LocaleProviderAdapter.Type defaultLocaleProviderAdapter;
     
         /**
          * Adapter lookup cache.
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java
    --- a/jdk/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -164,6 +164,6 @@
             return new HashSet<>();
         }
     
    -    private volatile Map lookup = null;
    +    private volatile Map lookup;
         private volatile Set keyset;
     }
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java
    --- a/jdk/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -47,7 +47,7 @@
         int fdVal;
     
         // ID of native thread doing write, for signalling
    -    private volatile long thread = 0;
    +    private volatile long thread;
     
         // Lock held by current reading thread
         private final Object lock = new Object();
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java
    --- a/jdk/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -47,7 +47,7 @@
         int fdVal;
     
         // ID of native thread doing read, for signalling
    -    private volatile long thread = 0;
    +    private volatile long thread;
     
         // Lock held by current reading thread
         private final Object lock = new Object();
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java
    --- a/jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -52,7 +52,7 @@
         private Map mimeTypeMap;
     
         // set to true when file loaded
    -    private volatile boolean loaded = false;
    +    private volatile boolean loaded;
     
         public MimeTypesFileTypeDetector(Path filePath) {
             mimeTypesFile = filePath;
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystemProvider.java
    --- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystemProvider.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystemProvider.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -69,15 +69,16 @@
         private void checkUri(URI uri) {
             if (!uri.getScheme().equalsIgnoreCase(getScheme()))
                 throw new IllegalArgumentException("URI does not match this provider");
    -        if (uri.getAuthority() != null)
    +        if (uri.getRawAuthority() != null)
                 throw new IllegalArgumentException("Authority component present");
    -        if (uri.getPath() == null)
    +        String path = uri.getPath();
    +        if (path == null)
                 throw new IllegalArgumentException("Path component is undefined");
    -        if (!uri.getPath().equals("/"))
    +        if (!path.equals("/"))
                 throw new IllegalArgumentException("Path component should be '/'");
    -        if (uri.getQuery() != null)
    +        if (uri.getRawQuery() != null)
                 throw new IllegalArgumentException("Query component present");
    -        if (uri.getFragment() != null)
    +        if (uri.getRawFragment() != null)
                 throw new IllegalArgumentException("Fragment component present");
         }
     
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java
    --- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -49,11 +49,11 @@
             String scheme = uri.getScheme();
             if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
                 throw new IllegalArgumentException("URI scheme is not \"file\"");
    -        if (uri.getAuthority() != null)
    +        if (uri.getRawAuthority() != null)
                 throw new IllegalArgumentException("URI has an authority component");
    -        if (uri.getFragment() != null)
    +        if (uri.getRawFragment() != null)
                 throw new IllegalArgumentException("URI has a fragment component");
    -        if (uri.getQuery() != null)
    +        if (uri.getRawQuery() != null)
                 throw new IllegalArgumentException("URI has a query component");
     
             // compatibility with java.io.File
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java
    --- a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -119,7 +119,7 @@
     
         // Lock for interrupt triggering and clearing
         private final Object interruptLock = new Object();
    -    private volatile boolean interruptTriggered = false;
    +    private volatile boolean interruptTriggered;
     
         WindowsSelectorImpl(SelectorProvider sp) throws IOException {
             super(sp);
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java
    --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -61,15 +61,16 @@
         private void checkUri(URI uri) {
             if (!uri.getScheme().equalsIgnoreCase(getScheme()))
                 throw new IllegalArgumentException("URI does not match this provider");
    -        if (uri.getAuthority() != null)
    +        if (uri.getRawAuthority() != null)
                 throw new IllegalArgumentException("Authority component present");
    -        if (uri.getPath() == null)
    +        String path = uri.getPath();
    +        if (path == null)
                 throw new IllegalArgumentException("Path component is undefined");
    -        if (!uri.getPath().equals("/"))
    +        if (!path.equals("/"))
                 throw new IllegalArgumentException("Path component should be '/'");
    -        if (uri.getQuery() != null)
    +        if (uri.getRawQuery() != null)
                 throw new IllegalArgumentException("Query component present");
    -        if (uri.getFragment() != null)
    +        if (uri.getRawFragment() != null)
                 throw new IllegalArgumentException("Fragment component present");
         }
     
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUriSupport.java
    --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUriSupport.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUriSupport.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -123,16 +123,16 @@
             String scheme = uri.getScheme();
             if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
                 throw new IllegalArgumentException("URI scheme is not \"file\"");
    -        if (uri.getFragment() != null)
    +        if (uri.getRawFragment() != null)
                 throw new IllegalArgumentException("URI has a fragment component");
    -        if (uri.getQuery() != null)
    +        if (uri.getRawQuery() != null)
                 throw new IllegalArgumentException("URI has a query component");
             String path = uri.getPath();
             if (path.equals(""))
                 throw new IllegalArgumentException("URI path component is empty");
     
             // UNC
    -        String auth = uri.getAuthority();
    +        String auth = uri.getRawAuthority();
             if (auth != null && !auth.equals("")) {
                 String host = uri.getHost();
                 if (host == null)
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.datatransfer/share/classes/java/awt/datatransfer/DataFlavor.java
    --- a/jdk/src/java.datatransfer/share/classes/java/awt/datatransfer/DataFlavor.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.datatransfer/share/classes/java/awt/datatransfer/DataFlavor.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -289,6 +289,8 @@
          *     representationClass = String
          *     mimeType           = "text/html"
          * 
    +     *
    +     * @since 1.8
          */
         public static DataFlavor selectionHtmlFlavor = initHtmlDataFlavor("selection");
     
    @@ -301,6 +303,8 @@
          *     representationClass = String
          *     mimeType           = "text/html"
          * 
    +     *
    +     * @since 1.8
          */
         public static DataFlavor fragmentHtmlFlavor = initHtmlDataFlavor("fragment");
     
    @@ -314,6 +318,8 @@
          *     representationClass = String
          *     mimeType           = "text/html"
          * 
    +     *
    +     * @since 1.8
          */
         public static  DataFlavor allHtmlFlavor = initHtmlDataFlavor("all");
     
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m
    --- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m	Wed Jul 05 21:10:34 2017 +0200
    @@ -24,6 +24,7 @@
      */
     
     #import 
    +#include 
     
     #import "CMenuItem.h"
     #import "CMenu.h"
    @@ -40,7 +41,7 @@
     @implementation CMenuItem
     
     - (id) initWithPeer:(jobject)peer asSeparator: (NSNumber *) asSeparator{
    -AWT_ASSERT_APPKIT_THREAD;
    +    AWT_ASSERT_APPKIT_THREAD;
         self = [super initWithPeer:peer];
         if (self) {
             if ([asSeparator boolValue]) {
    @@ -63,13 +64,48 @@
     - (BOOL) worksWhenModal {
         return YES;
     }
    +// This is a method written using Carbon framework methods to remove
    +// All modifiers including "Shift" modifier.
    +// Example 1: Shortcut set is "Command Shift m" returns "m"
    +// Example 2: Shortcut set is "Command m" returns "m"
    +// Example 3: Shortcut set is "Alt Shift ," returns ","
    +
    +CFStringRef createStringForKey(CGKeyCode keyCode)
    +{
    +    TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
    +//  currentKeyboard now contains the current input source
    +    CFDataRef layoutData =
    +    TISGetInputSourceProperty(currentKeyboard,
    +                              kTISPropertyUnicodeKeyLayoutData);
    +//  the UNICODE keyLayout is fetched from currentKeyboard in layoutData
    +    const UCKeyboardLayout *keyboardLayout =
    +    (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
    +//  A read-only data pointer is fetched from layoutData
    +    UInt32 keysDown = 0;
    +    UniChar chars[4];
    +    UniCharCount realLength;
    +    
    +    UCKeyTranslate(keyboardLayout,
    +                   keyCode,
    +                   kUCKeyActionDisplay,
    +                   0,
    +                   LMGetKbdType(),
    +                   kUCKeyTranslateNoDeadKeysBit,
    +                   &keysDown,
    +                   sizeof(chars) / sizeof(chars[0]),
    +                   &realLength,
    +                   chars);
    +    CFRelease(currentKeyboard);
    +//  Converts keyCode, modifier and dead-key state into UNICODE characters
    +    return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);
    +}
     
     // Events
     - (void)handleAction:(NSMenuItem *)sender {
    -AWT_ASSERT_APPKIT_THREAD;
    +    AWT_ASSERT_APPKIT_THREAD;
         JNIEnv *env = [ThreadUtilities getJNIEnv];
    -JNF_COCOA_ENTER(env);
    -
    +    JNF_COCOA_ENTER(env);
    +    
         // If we are called as a result of user pressing a shortcut, do nothing,
         // because AVTView has already sent corresponding key event to the Java
         // layer from performKeyEquivalent.
    @@ -82,31 +118,37 @@
         NSEvent *currEvent = [[NSApplication sharedApplication] currentEvent];
         if ([currEvent type] == NSKeyDown) {
             NSString *menuKey = [sender keyEquivalent];
    -        NSString *eventKey = [currEvent charactersIgnoringModifiers];
    -
    -        // Apple uses characters from private Unicode range for some of the
    -        // keys, so we need to do the same translation here that we do
    -        // for the regular key down events
    -        if ([eventKey length] == 1) {
    -            unichar origChar = [eventKey characterAtIndex:0];
    -            unichar newChar =  NsCharToJavaChar(origChar, 0);
    -            if (newChar == java_awt_event_KeyEvent_CHAR_UNDEFINED) {
    -                newChar = origChar;
    -            }
    -
    -            eventKey = [NSString stringWithCharacters: &newChar length: 1];
    -        }
    -
    +//      If shortcut is "Command Shift ," the menuKey gets the value ","
    +//      But [currEvent charactersIgnoringModifiers]; returns "<" and not ","
    +//      because the charactersIgnoreingModifiers does not ignore "Shift"
    +//      So a shortcut like "Command Shift m" will return "M" where as the
    +//      MenuKey will have the value "m". To remove this issue the below
    +//      createStringForKey is used.
    +        NSString *eventKey = createStringForKey([currEvent keyCode]);
    +        
    +//      Apple uses characters from private Unicode range for some of the
    +//      keys, so we need to do the same translation here that we do
    +//      for the regular key down events
    +                if ([eventKey length] == 1) {
    +                    unichar origChar = [eventKey characterAtIndex:0];
    +                    unichar newChar =  NsCharToJavaChar(origChar, 0);
    +                    if (newChar == java_awt_event_KeyEvent_CHAR_UNDEFINED) {
    +                        newChar = origChar;
    +                    }
    +        
    +                    eventKey = [NSString stringWithCharacters: &newChar length: 1];
    +                }
    +        
             NSWindow *keyWindow = [NSApp keyWindow];
             if ([menuKey isEqualToString:eventKey] && keyWindow != nil) {
                 return;
             }
         }
    -
    +    
         if (fIsCheckbox) {
             static JNF_CLASS_CACHE(jc_CCheckboxMenuItem, "sun/lwawt/macosx/CCheckboxMenuItem");
             static JNF_MEMBER_CACHE(jm_ckHandleAction, jc_CCheckboxMenuItem, "handleAction", "(Z)V");
    -
    +        
             // Send the opposite of what's currently checked -- the action
             // indicates what state we're going to.
             NSInteger state = [sender state];
    @@ -115,26 +157,26 @@
         } else {
             static JNF_CLASS_CACHE(jc_CMenuItem, "sun/lwawt/macosx/CMenuItem");
             static JNF_MEMBER_CACHE(jm_handleAction, jc_CMenuItem, "handleAction", "(JI)V"); // AWT_THREADING Safe (event)
    -
    +        
             NSUInteger modifiers = [currEvent modifierFlags];
             jint javaModifiers = NsKeyModifiersToJavaModifiers(modifiers, NO);
    -
    +        
             JNFCallVoidMethod(env, fPeer, jm_handleAction, UTC(currEvent), javaModifiers); // AWT_THREADING Safe (event)
         }
    -JNF_COCOA_EXIT(env);
    +    JNF_COCOA_EXIT(env);
     }
     
     - (void) setJavaLabel:(NSString *)theLabel shortcut:(NSString *)theKeyEquivalent modifierMask:(jint)modifiers {
    -
    +    
         NSUInteger modifierMask = 0;
    -
    +    
         if (![theKeyEquivalent isEqualToString:@""]) {
             // Force the key equivalent to lower case if not using the shift key.
             // Otherwise AppKit will draw a Shift glyph in the menu.
             if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) == 0) {
                 theKeyEquivalent = [theKeyEquivalent lowercaseString];
             }
    -
    +        
             // Hack for the question mark -- SHIFT and / means use the question mark.
             if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) != 0 &&
                 [theKeyEquivalent isEqualToString:@"/"])
    @@ -142,10 +184,10 @@
                 theKeyEquivalent = @"?";
                 modifiers &= ~java_awt_event_KeyEvent_SHIFT_MASK;
             }
    -
    +        
             modifierMask = JavaModifiersToNsKeyModifiers(modifiers, NO);
         }
    -
    +    
         [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
             [fMenuItem setKeyEquivalent:theKeyEquivalent];
             [fMenuItem setKeyEquivalentModifierMask:modifierMask];
    @@ -154,14 +196,14 @@
     }
     
     - (void) setJavaImage:(NSImage *)theImage {
    -
    +    
         [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
             [fMenuItem setImage:theImage];
         }];
     }
     
     - (void) setJavaToolTipText:(NSString *)theText {
    -
    +    
         [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
             [fMenuItem setToolTip:theText];
         }];
    @@ -169,11 +211,11 @@
     
     
     - (void)setJavaEnabled:(BOOL) enabled {
    -
    +    
         [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
             @synchronized(self) {
                 fIsEnabled = enabled;
    -
    +            
                 // Warning:  This won't work if the parent menu is disabled.
                 // See [CMenu syncFromJava]. We still need to call it here so
                 // the NSMenuItem itself gets properly updated.
    @@ -183,7 +225,7 @@
     }
     
     - (BOOL)isEnabled {
    -
    +    
         BOOL enabled = NO;
         @synchronized(self) {
             enabled = fIsEnabled;
    @@ -193,7 +235,7 @@
     
     
     - (void)setJavaState:(BOOL)newState {
    -
    +    
         [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
             [fMenuItem setState:(newState ? NSOnState : NSOffState)];
         }];
    @@ -207,7 +249,7 @@
     - (void)dealloc {
         [fMenuItem release];
         fMenuItem = nil;
    -
    +    
         [super dealloc];
     }
     
    @@ -240,7 +282,7 @@
     /** Convert a Java keycode for SetMenuItemCmd */
     static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) {
         unichar macKey = 0;
    -
    +    
         if ((awtKey >= java_awt_event_KeyEvent_VK_0 && awtKey <= java_awt_event_KeyEvent_VK_9) ||
             (awtKey >= java_awt_event_KeyEvent_VK_A && awtKey <= java_awt_event_KeyEvent_VK_Z))
         {
    @@ -255,68 +297,68 @@
         } else {
             // Special characters
             switch (awtKey) {
    -        case java_awt_event_KeyEvent_VK_BACK_QUOTE      : macKey = '`'; break;
    -        case java_awt_event_KeyEvent_VK_QUOTE           : macKey = '\''; break;
    -
    -        case java_awt_event_KeyEvent_VK_ESCAPE          : macKey = 0x1B; break;
    -        case java_awt_event_KeyEvent_VK_SPACE           : macKey = ' '; break;
    -        case java_awt_event_KeyEvent_VK_PAGE_UP         : macKey = NSPageUpFunctionKey; break;
    -        case java_awt_event_KeyEvent_VK_PAGE_DOWN       : macKey = NSPageDownFunctionKey; break;
    -        case java_awt_event_KeyEvent_VK_END             : macKey = NSEndFunctionKey; break;
    -        case java_awt_event_KeyEvent_VK_HOME            : macKey = NSHomeFunctionKey; break;
    -
    -        case java_awt_event_KeyEvent_VK_LEFT            : macKey = NSLeftArrowFunctionKey; break;
    -        case java_awt_event_KeyEvent_VK_UP              : macKey = NSUpArrowFunctionKey; break;
    -        case java_awt_event_KeyEvent_VK_RIGHT           : macKey = NSRightArrowFunctionKey; break;
    -        case java_awt_event_KeyEvent_VK_DOWN            : macKey = NSDownArrowFunctionKey; break;
    -
    -        case java_awt_event_KeyEvent_VK_COMMA           : macKey = ','; break;
    -
    -        // Mac OS doesn't distinguish between the two '-' keys...
    -        case java_awt_event_KeyEvent_VK_MINUS           :
    -        case java_awt_event_KeyEvent_VK_SUBTRACT        : macKey = '-'; break;
    -
    -        // or the two '.' keys...
    -        case java_awt_event_KeyEvent_VK_DECIMAL         :
    -        case java_awt_event_KeyEvent_VK_PERIOD          : macKey = '.'; break;
    -
    -        // or the two '/' keys.
    -        case java_awt_event_KeyEvent_VK_DIVIDE          :
    -        case java_awt_event_KeyEvent_VK_SLASH           : macKey = '/'; break;
    -
    -        case java_awt_event_KeyEvent_VK_SEMICOLON       : macKey = ';'; break;
    -        case java_awt_event_KeyEvent_VK_EQUALS          : macKey = '='; break;
    -
    -        case java_awt_event_KeyEvent_VK_OPEN_BRACKET    : macKey = '['; break;
    -        case java_awt_event_KeyEvent_VK_BACK_SLASH      : macKey = '\\'; break;
    -        case java_awt_event_KeyEvent_VK_CLOSE_BRACKET   : macKey = ']'; break;
    -
    -        case java_awt_event_KeyEvent_VK_MULTIPLY        : macKey = '*'; break;
    -        case java_awt_event_KeyEvent_VK_ADD             : macKey = '+'; break;
    -
    -        case java_awt_event_KeyEvent_VK_HELP            : macKey = NSHelpFunctionKey; break;
    -        case java_awt_event_KeyEvent_VK_TAB             : macKey = NSTabCharacter; break;
    -        case java_awt_event_KeyEvent_VK_ENTER           : macKey = NSNewlineCharacter; break;
    -        case java_awt_event_KeyEvent_VK_BACK_SPACE      : macKey = NSBackspaceCharacter; break;
    -        case java_awt_event_KeyEvent_VK_DELETE          : macKey = NSDeleteCharacter; break;
    -        case java_awt_event_KeyEvent_VK_CLEAR           : macKey = NSClearDisplayFunctionKey; break;
    -        case java_awt_event_KeyEvent_VK_AMPERSAND       : macKey = '&'; break;
    -        case java_awt_event_KeyEvent_VK_ASTERISK        : macKey = '*'; break;
    -        case java_awt_event_KeyEvent_VK_QUOTEDBL        : macKey = '\"'; break;
    -        case java_awt_event_KeyEvent_VK_LESS            : macKey = '<'; break;
    -        case java_awt_event_KeyEvent_VK_GREATER         : macKey = '>'; break;
    -        case java_awt_event_KeyEvent_VK_BRACELEFT       : macKey = '{'; break;
    -        case java_awt_event_KeyEvent_VK_BRACERIGHT      : macKey = '}'; break;
    -        case java_awt_event_KeyEvent_VK_AT              : macKey = '@'; break;
    -        case java_awt_event_KeyEvent_VK_COLON           : macKey = ':'; break;
    -        case java_awt_event_KeyEvent_VK_CIRCUMFLEX      : macKey = '^'; break;
    -        case java_awt_event_KeyEvent_VK_DOLLAR          : macKey = '$'; break;
    -        case java_awt_event_KeyEvent_VK_EXCLAMATION_MARK : macKey = '!'; break;
    -        case java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS : macKey = '('; break;
    -        case java_awt_event_KeyEvent_VK_NUMBER_SIGN     : macKey = '#'; break;
    -        case java_awt_event_KeyEvent_VK_PLUS            : macKey = '+'; break;
    -        case java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS: macKey = ')'; break;
    -        case java_awt_event_KeyEvent_VK_UNDERSCORE      : macKey = '_'; break;
    +            case java_awt_event_KeyEvent_VK_BACK_QUOTE      : macKey = '`'; break;
    +            case java_awt_event_KeyEvent_VK_QUOTE           : macKey = '\''; break;
    +                
    +            case java_awt_event_KeyEvent_VK_ESCAPE          : macKey = 0x1B; break;
    +            case java_awt_event_KeyEvent_VK_SPACE           : macKey = ' '; break;
    +            case java_awt_event_KeyEvent_VK_PAGE_UP         : macKey = NSPageUpFunctionKey; break;
    +            case java_awt_event_KeyEvent_VK_PAGE_DOWN       : macKey = NSPageDownFunctionKey; break;
    +            case java_awt_event_KeyEvent_VK_END             : macKey = NSEndFunctionKey; break;
    +            case java_awt_event_KeyEvent_VK_HOME            : macKey = NSHomeFunctionKey; break;
    +                
    +            case java_awt_event_KeyEvent_VK_LEFT            : macKey = NSLeftArrowFunctionKey; break;
    +            case java_awt_event_KeyEvent_VK_UP              : macKey = NSUpArrowFunctionKey; break;
    +            case java_awt_event_KeyEvent_VK_RIGHT           : macKey = NSRightArrowFunctionKey; break;
    +            case java_awt_event_KeyEvent_VK_DOWN            : macKey = NSDownArrowFunctionKey; break;
    +                
    +            case java_awt_event_KeyEvent_VK_COMMA           : macKey = ','; break;
    +                
    +                // Mac OS doesn't distinguish between the two '-' keys...
    +            case java_awt_event_KeyEvent_VK_MINUS           :
    +            case java_awt_event_KeyEvent_VK_SUBTRACT        : macKey = '-'; break;
    +                
    +                // or the two '.' keys...
    +            case java_awt_event_KeyEvent_VK_DECIMAL         :
    +            case java_awt_event_KeyEvent_VK_PERIOD          : macKey = '.'; break;
    +                
    +                // or the two '/' keys.
    +            case java_awt_event_KeyEvent_VK_DIVIDE          :
    +            case java_awt_event_KeyEvent_VK_SLASH           : macKey = '/'; break;
    +                
    +            case java_awt_event_KeyEvent_VK_SEMICOLON       : macKey = ';'; break;
    +            case java_awt_event_KeyEvent_VK_EQUALS          : macKey = '='; break;
    +                
    +            case java_awt_event_KeyEvent_VK_OPEN_BRACKET    : macKey = '['; break;
    +            case java_awt_event_KeyEvent_VK_BACK_SLASH      : macKey = '\\'; break;
    +            case java_awt_event_KeyEvent_VK_CLOSE_BRACKET   : macKey = ']'; break;
    +                
    +            case java_awt_event_KeyEvent_VK_MULTIPLY        : macKey = '*'; break;
    +            case java_awt_event_KeyEvent_VK_ADD             : macKey = '+'; break;
    +                
    +            case java_awt_event_KeyEvent_VK_HELP            : macKey = NSHelpFunctionKey; break;
    +            case java_awt_event_KeyEvent_VK_TAB             : macKey = NSTabCharacter; break;
    +            case java_awt_event_KeyEvent_VK_ENTER           : macKey = NSNewlineCharacter; break;
    +            case java_awt_event_KeyEvent_VK_BACK_SPACE      : macKey = NSBackspaceCharacter; break;
    +            case java_awt_event_KeyEvent_VK_DELETE          : macKey = NSDeleteCharacter; break;
    +            case java_awt_event_KeyEvent_VK_CLEAR           : macKey = NSClearDisplayFunctionKey; break;
    +            case java_awt_event_KeyEvent_VK_AMPERSAND       : macKey = '&'; break;
    +            case java_awt_event_KeyEvent_VK_ASTERISK        : macKey = '*'; break;
    +            case java_awt_event_KeyEvent_VK_QUOTEDBL        : macKey = '\"'; break;
    +            case java_awt_event_KeyEvent_VK_LESS            : macKey = '<'; break;
    +            case java_awt_event_KeyEvent_VK_GREATER         : macKey = '>'; break;
    +            case java_awt_event_KeyEvent_VK_BRACELEFT       : macKey = '{'; break;
    +            case java_awt_event_KeyEvent_VK_BRACERIGHT      : macKey = '}'; break;
    +            case java_awt_event_KeyEvent_VK_AT              : macKey = '@'; break;
    +            case java_awt_event_KeyEvent_VK_COLON           : macKey = ':'; break;
    +            case java_awt_event_KeyEvent_VK_CIRCUMFLEX      : macKey = '^'; break;
    +            case java_awt_event_KeyEvent_VK_DOLLAR          : macKey = '$'; break;
    +            case java_awt_event_KeyEvent_VK_EXCLAMATION_MARK : macKey = '!'; break;
    +            case java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS : macKey = '('; break;
    +            case java_awt_event_KeyEvent_VK_NUMBER_SIGN     : macKey = '#'; break;
    +            case java_awt_event_KeyEvent_VK_PLUS            : macKey = '+'; break;
    +            case java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS: macKey = ')'; break;
    +            case java_awt_event_KeyEvent_VK_UNDERSCORE      : macKey = '_'; break;
             }
         }
         return macKey;
    @@ -330,27 +372,27 @@
     JNIEXPORT void JNICALL
     Java_sun_lwawt_macosx_CMenuItem_nativeSetLabel
     (JNIEnv *env, jobject peer,
    -     jlong menuItemObj, jstring label,
    -     jchar shortcutKey, jint shortcutKeyCode, jint mods)
    + jlong menuItemObj, jstring label,
    + jchar shortcutKey, jint shortcutKeyCode, jint mods)
     {
    -JNF_COCOA_ENTER(env);
    +    JNF_COCOA_ENTER(env);
         NSString *theLabel = JNFJavaToNSString(env, label);
         NSString *theKeyEquivalent = nil;
         unichar macKey = shortcutKey;
    -
    +    
         if (macKey == 0) {
             macKey = AWTKeyToMacShortcut(shortcutKeyCode, (mods & java_awt_event_KeyEvent_SHIFT_MASK) != 0);
         }
    -
    +    
         if (macKey != 0) {
             unichar equivalent[1] = {macKey};
             theKeyEquivalent = [NSString stringWithCharacters:equivalent length:1];
         } else {
             theKeyEquivalent = @"";
         }
    -
    +    
         [((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaLabel:theLabel shortcut:theKeyEquivalent modifierMask:mods];
    -JNF_COCOA_EXIT(env);
    +    JNF_COCOA_EXIT(env);
     }
     
     /*
    @@ -362,10 +404,10 @@
     Java_sun_lwawt_macosx_CMenuItem_nativeSetTooltip
     (JNIEnv *env, jobject peer, jlong menuItemObj, jstring tooltip)
     {
    -JNF_COCOA_ENTER(env);
    +    JNF_COCOA_ENTER(env);
         NSString *theTooltip = JNFJavaToNSString(env, tooltip);
         [((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaToolTipText:theTooltip];
    -JNF_COCOA_EXIT(env);
    +    JNF_COCOA_EXIT(env);
     }
     
     /*
    @@ -377,9 +419,9 @@
     Java_sun_lwawt_macosx_CMenuItem_nativeSetImage
     (JNIEnv *env, jobject peer, jlong menuItemObj, jlong image)
     {
    -JNF_COCOA_ENTER(env);
    +    JNF_COCOA_ENTER(env);
         [((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaImage:(NSImage*)jlong_to_ptr(image)];
    -JNF_COCOA_EXIT(env);
    +    JNF_COCOA_EXIT(env);
     }
     
     /*
    @@ -389,38 +431,38 @@
      */
     JNIEXPORT jlong JNICALL
     Java_sun_lwawt_macosx_CMenuItem_nativeCreate
    -    (JNIEnv *env, jobject peer, jlong parentCMenuObj, jboolean isSeparator)
    +(JNIEnv *env, jobject peer, jlong parentCMenuObj, jboolean isSeparator)
     {
    -
    +    
         CMenuItem *aCMenuItem = nil;
         CMenu *parentCMenu = (CMenu *)jlong_to_ptr(parentCMenuObj);
    -JNF_COCOA_ENTER(env);
    -
    +    JNF_COCOA_ENTER(env);
    +    
         jobject cPeerObjGlobal = (*env)->NewGlobalRef(env, peer);
    -
    +    
         NSMutableArray *args = nil;
    -
    +    
         // Create a new item....
         if (isSeparator == JNI_TRUE) {
             args = [[NSMutableArray alloc] initWithObjects:[NSValue valueWithBytes:&cPeerObjGlobal objCType:@encode(jobject)], [NSNumber numberWithBool:YES],  nil];
         } else {
             args = [[NSMutableArray alloc] initWithObjects:[NSValue valueWithBytes:&cPeerObjGlobal objCType:@encode(jobject)], [NSNumber numberWithBool:NO],  nil];
         }
    -
    +    
         [ThreadUtilities performOnMainThread:@selector(_createMenuItem_OnAppKitThread:) on:[CMenuItem alloc] withObject:args waitUntilDone:YES];
    -
    +    
         aCMenuItem = (CMenuItem *)[args objectAtIndex: 0];
    -
    +    
         if (aCMenuItem == nil) {
             return 0L;
         }
    -
    +    
         // and add it to the parent item.
         [parentCMenu addJavaMenuItem: aCMenuItem];
    -
    +    
         // setLabel will be called after creation completes.
    -
    -JNF_COCOA_EXIT(env);
    +    
    +    JNF_COCOA_EXIT(env);
         return ptr_to_jlong(aCMenuItem);
     }
     
    @@ -433,10 +475,10 @@
     Java_sun_lwawt_macosx_CMenuItem_nativeSetEnabled
     (JNIEnv *env, jobject peer, jlong menuItemObj, jboolean enable)
     {
    -JNF_COCOA_ENTER(env);
    +    JNF_COCOA_ENTER(env);
         CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);
         [item setJavaEnabled: (enable == JNI_TRUE)];
    -JNF_COCOA_EXIT(env);
    +    JNF_COCOA_EXIT(env);
     }
     
     /*
    @@ -448,10 +490,10 @@
     Java_sun_lwawt_macosx_CCheckboxMenuItem_nativeSetState
     (JNIEnv *env, jobject peer, jlong menuItemObj, jboolean state)
     {
    -JNF_COCOA_ENTER(env);
    +    JNF_COCOA_ENTER(env);
         CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);
         [item setJavaState: (state == JNI_TRUE)];
    -JNF_COCOA_EXIT(env);
    +    JNF_COCOA_EXIT(env);
     }
     
     /*
    @@ -463,8 +505,8 @@
     Java_sun_lwawt_macosx_CCheckboxMenuItem_nativeSetIsCheckbox
     (JNIEnv *env, jobject peer, jlong menuItemObj)
     {
    -JNF_COCOA_ENTER(env);
    +    JNF_COCOA_ENTER(env);
         CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);
         [item setIsCheckbox];
    -JNF_COCOA_EXIT(env);
    +    JNF_COCOA_EXIT(env);
     }
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java
    --- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -874,13 +874,13 @@
                 return chroma;
             }
     
    -        boolean idsAreJFIF = true;
    +        boolean idsAreJFIF = false;
     
    -        for (int i = 0; i < sof.componentSpecs.length; i++) {
    -            int id = sof.componentSpecs[i].componentId;
    -            if ((id < 1) || (id >= sof.componentSpecs.length)) {
    -                idsAreJFIF = false;
    -            }
    +        int cid0 = sof.componentSpecs[0].componentId;
    +        int cid1 = sof.componentSpecs[1].componentId;
    +        int cid2 = sof.componentSpecs[2].componentId;
    +        if ((cid0 == 1) && (cid1 == 2) && (cid2 == 3)) {
    +            idsAreJFIF = true;
             }
     
             if (idsAreJFIF) {
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java
    --- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -193,7 +193,17 @@
     
             // Return to end of chunk and flush to minimize buffering
             stream.seek(pos);
    -        stream.flushBefore(pos);
    +        try {
    +            stream.flushBefore(pos);
    +        } catch (IOException e) {
    +            /*
    +             * If flushBefore() fails we try to access startPos in finally
    +             * block of write_IDAT(). We should update startPos to avoid
    +             * IndexOutOfBoundException while seek() is happening.
    +             */
    +            this.startPos = stream.getStreamPosition();
    +            throw e;
    +        }
         }
     
         public int read() throws IOException {
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java
    --- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -472,6 +472,14 @@
                 // Read tag number, value type, and value count.
                 int tagNumber = stream.readUnsignedShort();
                 int type = stream.readUnsignedShort();
    +            int sizeOfType;
    +            try {
    +                sizeOfType = TIFFTag.getSizeOfType(type);
    +            } catch (IllegalArgumentException ignored) {
    +                // Continue with the next IFD entry.
    +                stream.skipBytes(4);
    +                continue;
    +            }
                 int count = (int)stream.readUnsignedInt();
     
                 // Get the associated TIFFTag.
    @@ -510,14 +518,14 @@
                     }
                 }
     
    -            int size = count*TIFFTag.getSizeOfType(type);
    +            int size = count*sizeOfType;
                 if (size > 4 || tag.isIFDPointer()) {
                     // The IFD entry value is a pointer to the actual field value.
                     long offset = stream.readUnsignedInt();
     
                     // Check whether the the field value is within the stream.
                     if (haveStreamLength && offset + size > streamLength) {
    -                    throw new IIOException("Field data is past end-of-stream");
    +                    continue;
                     }
     
                     // Add a TIFFIFDEntry as a placeholder. This avoids a mark,
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java
    --- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -205,6 +205,10 @@
         // Next available space.
         private long nextSpace = 0L;
     
    +    private long prevStreamPosition;
    +    private long prevHeaderPosition;
    +    private long prevNextSpace;
    +
         // Whether a sequence is being written.
         private boolean isWritingSequence = false;
         private boolean isInsertingEmpty = false;
    @@ -2300,7 +2304,14 @@
         public void write(IIOMetadata sm,
                           IIOImage iioimage,
                           ImageWriteParam p) throws IOException {
    +        if (stream == null) {
    +            throw new IllegalStateException("output == null!");
    +        }
    +        markPositions();
             write(sm, iioimage, p, true, true);
    +        if (abortRequested()) {
    +            resetPositions();
    +        }
         }
     
         private void writeHeader() throws IOException {
    @@ -2333,7 +2344,7 @@
                 throw new IllegalStateException("output == null!");
             }
             if (iioimage == null) {
    -            throw new NullPointerException("image == null!");
    +            throw new IllegalArgumentException("image == null!");
             }
             if(iioimage.hasRaster() && !canWriteRasters()) {
                 throw new UnsupportedOperationException
    @@ -2767,7 +2778,7 @@
                 throw new IllegalStateException("Output not set!");
             }
             if (image == null) {
    -            throw new NullPointerException("image == null!");
    +            throw new IllegalArgumentException("image == null!");
             }
     
             // Locate the position of the old IFD (ifd) and the location
    @@ -2779,9 +2790,16 @@
             // imageIndex is < -1 or is too big thereby satisfying the spec.
             locateIFD(imageIndex, ifdpos, ifd);
     
    +        markPositions();
    +
             // Seek to the position containing the pointer to the old IFD.
             stream.seek(ifdpos[0]);
     
    +        // Save the previous pointer value in case of abort.
    +        stream.mark();
    +        long prevPointerValue = stream.readUnsignedInt();
    +        stream.reset();
    +
             // Update next space pointer in anticipation of next write.
             if(ifdpos[0] + 4 > nextSpace) {
                 nextSpace = ifdpos[0] + 4;
    @@ -2805,6 +2823,12 @@
             // Update the new IFD to point to the old IFD.
             stream.writeInt((int)ifd[0]);
             // Don't need to update nextSpace here as already done in write().
    +
    +        if (abortRequested()) {
    +            stream.seek(ifdpos[0]);
    +            stream.writeInt((int)prevPointerValue);
    +            resetPositions();
    +        }
         }
     
         // ----- BEGIN insert/writeEmpty methods -----
    @@ -2834,7 +2858,7 @@
             }
     
             if(imageType == null) {
    -            throw new NullPointerException("imageType == null!");
    +            throw new IllegalArgumentException("imageType == null!");
             }
     
             if(width < 1 || height < 1) {
    @@ -2891,6 +2915,10 @@
                                       IIOMetadata imageMetadata,
                                       List thumbnails,
                                       ImageWriteParam param) throws IOException {
    +        if (stream == null) {
    +            throw new IllegalStateException("output == null!");
    +        }
    +
             checkParamsEmpty(imageType, width, height, thumbnails);
     
             this.isWritingEmpty = true;
    @@ -2901,8 +2929,12 @@
                                0, 0, emptySM.getWidth(), emptySM.getHeight(),
                                emptySM, imageType.getColorModel());
     
    +        markPositions();
             write(streamMetadata, new IIOImage(emptyImage, null, imageMetadata),
                   param, true, false);
    +        if (abortRequested()) {
    +            resetPositions();
    +        }
         }
     
         public void endInsertEmpty() throws IOException {
    @@ -3015,7 +3047,7 @@
                     throw new IllegalStateException("Output not set!");
                 }
                 if (region == null) {
    -                throw new NullPointerException("region == null!");
    +                throw new IllegalArgumentException("region == null!");
                 }
                 if (region.getWidth() < 1) {
                     throw new IllegalArgumentException("region.getWidth() < 1!");
    @@ -3200,7 +3232,7 @@
                 }
     
                 if (image == null) {
    -                throw new NullPointerException("image == null!");
    +                throw new IllegalArgumentException("image == null!");
                 }
     
                 if (!inReplacePixelsNest) {
    @@ -3559,6 +3591,20 @@
     
         // ----- END replacePixels methods -----
     
    +    // Save stream positions for use when aborted.
    +    private void markPositions() throws IOException {
    +        prevStreamPosition = stream.getStreamPosition();
    +        prevHeaderPosition = headerPosition;
    +        prevNextSpace = nextSpace;
    +    }
    +
    +    // Reset to positions saved by markPositions().
    +    private void resetPositions() throws IOException {
    +        stream.seek(prevStreamPosition);
    +        headerPosition = prevHeaderPosition;
    +        nextSpace = prevNextSpace;
    +    }
    +
         public void reset() {
             super.reset();
     
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java
    --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -25,10 +25,11 @@
     
     package com.sun.media.sound;
     
    +import java.util.Objects;
    +
     import javax.sound.midi.MidiDevice;
     import javax.sound.midi.spi.MidiDeviceProvider;
     
    -
     /**
      * Super class for MIDI input or output device provider.
      *
    @@ -127,7 +128,8 @@
         }
     
         @Override
    -    public final MidiDevice getDevice(MidiDevice.Info info) {
    +    public final MidiDevice getDevice(final MidiDevice.Info info) {
    +        Objects.requireNonNull(info);
             if (info instanceof Info) {
                 readDeviceInfos();
                 MidiDevice[] devices = getDeviceCache();
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java
    --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -22,6 +22,7 @@
      * or visit www.oracle.com if you need additional information or have any
      * questions.
      */
    +
     package com.sun.media.sound;
     
     import java.io.BufferedReader;
    @@ -32,6 +33,8 @@
     import java.net.URL;
     import java.net.URLClassLoader;
     import java.util.ArrayList;
    +import java.util.Objects;
    +
     import javax.sound.midi.InvalidMidiDataException;
     import javax.sound.midi.Soundbank;
     import javax.sound.midi.spi.SoundbankReader;
    @@ -112,6 +115,7 @@
     
         public Soundbank getSoundbank(InputStream stream)
                 throws InvalidMidiDataException, IOException {
    +        Objects.requireNonNull(stream);
             return null;
         }
     
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java
    --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -25,6 +25,8 @@
     
     package com.sun.media.sound;
     
    +import java.util.Objects;
    +
     import javax.sound.midi.MidiDevice;
     import javax.sound.midi.spi.MidiDeviceProvider;
     
    @@ -42,6 +44,7 @@
     
         @Override
         public MidiDevice getDevice(final MidiDevice.Info info) {
    +        Objects.requireNonNull(info);
             if (RealTimeSequencer.info.equals(info)) {
                 return new RealTimeSequencer();
             }
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java
    --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -25,6 +25,8 @@
     
     package com.sun.media.sound;
     
    +import java.util.Objects;
    +
     import javax.sound.midi.MidiDevice;
     import javax.sound.midi.spi.MidiDeviceProvider;
     
    @@ -42,6 +44,7 @@
     
         @Override
         public MidiDevice getDevice(final MidiDevice.Info info) {
    +        Objects.requireNonNull(info);
             if (SoftSynthesizer.info.equals(info)) {
                 return new SoftSynthesizer();
             }
    diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java
    --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java	Wed Dec 23 15:41:50 2015 -0800
    +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java	Wed Jul 05 21:10:34 2017 +0200
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -25,21 +25,22 @@
     
     package com.sun.media.sound;
     
    +import java.io.ByteArrayInputStream;
    +import java.io.ByteArrayOutputStream;
     import java.io.DataOutputStream;
    -import java.io.PipedInputStream;
    -import java.io.PipedOutputStream;
    -import java.io.ByteArrayOutputStream;
    -import java.io.ByteArrayInputStream;
    -import java.io.SequenceInputStream;
     import java.io.File;
     import java.io.FileOutputStream;
    +import java.io.IOException;
     import java.io.InputStream;
    -import java.io.IOException;
     import java.io.OutputStream;
    +import java.io.PipedInputStream;
    +import java.io.PipedOutputStream;
    +import java.io.SequenceInputStream;
    +import java.util.Objects;
     
     import javax.sound.midi.InvalidMidiDataException;
    +import javax.sound.midi.MetaMessage;
     import javax.sound.midi.MidiEvent;
    -import javax.sound.midi.MetaMessage;
     import javax.sound.midi.Sequence;
     import javax.sound.midi.ShortMessage;
     import javax.sound.midi.SysexMessage;
    @@ -115,24 +116,16 @@
             return typesArray;
         }
     
    -    public boolean isFileTypeSupported(int type) {
    -        for(int i=0; i= 0) ? columns : 0;
         }
     
    @@ -297,13 +297,32 @@
          * @see         java.awt.TextComponent#getText
          */
         public void setText(String t) {
    -        super.setText(t);
    +        super.setText(replaceEOL(t));
     
             // This could change the preferred size of the Component.
             invalidateIfValid();
         }
     
         /**
    +     * Replaces EOL characters from the text variable with a space character.
    +     * @param       text   the new text.
    +     * @return      Returns text after replacing EOL characters.
    +     */
    +    private static String replaceEOL(String text) {
    +        if (text == null) {
    +            return text;
    +        }
    +        String[] strEOLs = {System.lineSeparator(), "\n"};
    +        for (String eol : strEOLs) {
    +            if (text.contains(eol)) {
    +                text = text.replace(eol, " ");
    +            }
    +        }
    +        return text;
    +    }
    +
    +
    +    /**
          * Indicates whether or not this text field has a
          * character set for echoing.
          * 

    @@ -704,6 +723,7 @@ { // HeadlessException will be thrown by TextComponent's readObject s.defaultReadObject(); + text = replaceEOL(text); // Make sure the state we just read in for columns has legal values if (columns < 0) { diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java --- a/jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java Wed Jul 05 21:10:34 2017 +0200 @@ -564,9 +564,12 @@ if (stream != null) { stream.mark(); } - canDecode = spi.canDecodeInput(input); - if (stream != null) { - stream.reset(); + try { + canDecode = spi.canDecodeInput(input); + } finally { + if (stream != null) { + stream.reset(); + } } return canDecode; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java --- a/jdk/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Properties; import java.util.Set; @@ -179,10 +180,12 @@ * due to resource restrictions * @throws IllegalArgumentException if the info object does not represent a * MIDI device installed on the system + * @throws NullPointerException if {@code info} is {@code null} * @see #getMidiDeviceInfo */ public static MidiDevice getMidiDevice(final MidiDevice.Info info) throws MidiUnavailableException { + Objects.requireNonNull(info); for (final MidiDeviceProvider provider : getMidiDeviceProviders()) { if (provider.isDeviceSupported(info)) { return provider.getDevice(info); @@ -447,11 +450,13 @@ * @throws InvalidMidiDataException if the stream does not point to valid * MIDI soundbank data recognized by the system * @throws IOException if an I/O error occurred when loading the soundbank + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ - public static Soundbank getSoundbank(InputStream stream) - throws InvalidMidiDataException, IOException { + public static Soundbank getSoundbank(final InputStream stream) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(stream); SoundbankReader sp = null; Soundbank s = null; @@ -479,9 +484,11 @@ * @throws InvalidMidiDataException if the URL does not point to valid MIDI * soundbank data recognized by the system * @throws IOException if an I/O error occurred when loading the soundbank + * @throws NullPointerException if {@code url} is {@code null} */ - public static Soundbank getSoundbank(URL url) - throws InvalidMidiDataException, IOException { + public static Soundbank getSoundbank(final URL url) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(url); SoundbankReader sp = null; Soundbank s = null; @@ -509,9 +516,11 @@ * @throws InvalidMidiDataException if the {@code File} does not point to * valid MIDI soundbank data recognized by the system * @throws IOException if an I/O error occurred when loading the soundbank + * @throws NullPointerException if {@code file} is {@code null} */ - public static Soundbank getSoundbank(File file) - throws InvalidMidiDataException, IOException { + public static Soundbank getSoundbank(final File file) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(file); SoundbankReader sp = null; Soundbank s = null; @@ -556,13 +565,15 @@ * MIDI file data recognized by the system * @throws IOException if an I/O exception occurs while accessing the * stream + * @throws NullPointerException if {@code stream} is {@code null} * @see #getMidiFileFormat(URL) * @see #getMidiFileFormat(File) * @see InputStream#markSupported * @see InputStream#mark */ - public static MidiFileFormat getMidiFileFormat(InputStream stream) - throws InvalidMidiDataException, IOException { + public static MidiFileFormat getMidiFileFormat(final InputStream stream) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(stream); List providers = getMidiFileReaders(); MidiFileFormat format = null; @@ -602,11 +613,13 @@ * @throws InvalidMidiDataException if the URL does not point to valid MIDI * file data recognized by the system * @throws IOException if an I/O exception occurs while accessing the URL + * @throws NullPointerException if {@code url} is {@code null} * @see #getMidiFileFormat(InputStream) * @see #getMidiFileFormat(File) */ - public static MidiFileFormat getMidiFileFormat(URL url) - throws InvalidMidiDataException, IOException { + public static MidiFileFormat getMidiFileFormat(final URL url) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(url); List providers = getMidiFileReaders(); MidiFileFormat format = null; @@ -646,11 +659,13 @@ * @throws InvalidMidiDataException if the {@code File} does not point to * valid MIDI file data recognized by the system * @throws IOException if an I/O exception occurs while accessing the file + * @throws NullPointerException if {@code file} is {@code null} * @see #getMidiFileFormat(InputStream) * @see #getMidiFileFormat(URL) */ - public static MidiFileFormat getMidiFileFormat(File file) - throws InvalidMidiDataException, IOException { + public static MidiFileFormat getMidiFileFormat(final File file) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(file); List providers = getMidiFileReaders(); MidiFileFormat format = null; @@ -699,11 +714,13 @@ * @throws InvalidMidiDataException if the stream does not point to valid * MIDI file data recognized by the system * @throws IOException if an I/O exception occurs while accessing the stream + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ - public static Sequence getSequence(InputStream stream) - throws InvalidMidiDataException, IOException { + public static Sequence getSequence(final InputStream stream) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(stream); List providers = getMidiFileReaders(); Sequence sequence = null; @@ -743,9 +760,11 @@ * @throws InvalidMidiDataException if the URL does not point to valid MIDI * file data recognized by the system * @throws IOException if an I/O exception occurs while accessing the URL + * @throws NullPointerException if {@code url} is {@code null} */ - public static Sequence getSequence(URL url) - throws InvalidMidiDataException, IOException { + public static Sequence getSequence(final URL url) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(url); List providers = getMidiFileReaders(); Sequence sequence = null; @@ -787,9 +806,11 @@ * @throws InvalidMidiDataException if the File does not point to valid MIDI * file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code file} is {@code null} */ - public static Sequence getSequence(File file) - throws InvalidMidiDataException, IOException { + public static Sequence getSequence(final File file) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(file); List providers = getMidiFileReaders(); Sequence sequence = null; @@ -870,8 +891,10 @@ * @param sequence the sequence for which MIDI file type support is queried * @return the set of unique supported file types. If no file types are * supported, returns an array of length 0. + * @throws NullPointerException if {@code sequence} is {@code null} */ - public static int[] getMidiFileTypes(Sequence sequence) { + public static int[] getMidiFileTypes(final Sequence sequence) { + Objects.requireNonNull(sequence); List providers = getMidiFileWriters(); Set allTypes = new HashSet<>(); @@ -903,8 +926,11 @@ * @param sequence the sequence for which file writing support is queried * @return {@code true} if the file type is supported for this sequence, * otherwise {@code false} + * @throws NullPointerException if {@code sequence} is {@code null} */ - public static boolean isFileTypeSupported(int fileType, Sequence sequence) { + public static boolean isFileTypeSupported(final int fileType, + final Sequence sequence) { + Objects.requireNonNull(sequence); List providers = getMidiFileWriters(); @@ -929,10 +955,15 @@ * @throws IOException if an I/O exception occurs * @throws IllegalArgumentException if the file format is not supported by * the system + * @throws NullPointerException if {@code in} or {@code out} are + * {@code null} * @see #isFileTypeSupported(int, Sequence) * @see #getMidiFileTypes(Sequence) */ - public static int write(Sequence in, int fileType, OutputStream out) throws IOException { + public static int write(final Sequence in, final int fileType, + final OutputStream out) throws IOException { + Objects.requireNonNull(in); + Objects.requireNonNull(out); List providers = getMidiFileWriters(); //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences @@ -963,10 +994,15 @@ * @throws IOException if an I/O exception occurs * @throws IllegalArgumentException if the file type is not supported by the * system + * @throws NullPointerException if {@code in} or {@code out} are + * {@code null} * @see #isFileTypeSupported(int, Sequence) * @see #getMidiFileTypes(Sequence) */ - public static int write(Sequence in, int type, File out) throws IOException { + public static int write(final Sequence in, final int type, final File out) + throws IOException { + Objects.requireNonNull(in); + Objects.requireNonNull(out); List providers = getMidiFileWriters(); //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java --- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package javax.sound.midi.spi; import java.util.Arrays; +import java.util.Objects; import javax.sound.midi.MidiDevice; @@ -46,8 +47,10 @@ * is queried * @return {@code true} if the specified device is supported, otherwise * {@code false} + * @throws NullPointerException if {@code info} is {@code null} */ public boolean isDeviceSupported(final MidiDevice.Info info) { + Objects.requireNonNull(info); return Arrays.asList(getDeviceInfo()).contains(info); } @@ -67,6 +70,7 @@ * @throws IllegalArgumentException if the info object specified does not * match the info object for a device supported by this * {@code MidiDeviceProvider} + * @throws NullPointerException if {@code info} is {@code null} */ public abstract MidiDevice getDevice(MidiDevice.Info info); } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileReader.java --- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileReader.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileReader.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,6 +60,7 @@ * @throws InvalidMidiDataException if the stream does not point to valid * MIDI file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ @@ -76,6 +77,7 @@ * @throws InvalidMidiDataException if the URL does not point to valid MIDI * file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code url} is {@code null} */ public abstract MidiFileFormat getMidiFileFormat(URL url) throws InvalidMidiDataException, IOException; @@ -90,6 +92,7 @@ * @throws InvalidMidiDataException if the {@code File} does not point to * valid MIDI file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code file} is {@code null} */ public abstract MidiFileFormat getMidiFileFormat(File file) throws InvalidMidiDataException, IOException; @@ -110,6 +113,7 @@ * @throws InvalidMidiDataException if the stream does not point to valid * MIDI file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ @@ -126,6 +130,7 @@ * @throws InvalidMidiDataException if the URL does not point to valid MIDI * file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code url} is {@code null} */ public abstract Sequence getSequence(URL url) throws InvalidMidiDataException, IOException; @@ -141,6 +146,7 @@ * @throws InvalidMidiDataException if the {@code File} does not point to * valid MIDI file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code file} is {@code null} */ public abstract Sequence getSequence(File file) throws InvalidMidiDataException, IOException; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileWriter.java --- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileWriter.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileWriter.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,6 +58,7 @@ * queried * @return array of file types. If no file types are supported, returns an * array of length 0. + * @throws NullPointerException if {@code sequence} is {@code null} */ public abstract int[] getMidiFileTypes(Sequence sequence); @@ -88,6 +89,7 @@ * @param sequence the sequence for which file writing support is queried * @return {@code true} if the file type is supported for this sequence, * otherwise {@code false} + * @throws NullPointerException if {@code sequence} is {@code null} */ public boolean isFileTypeSupported(int fileType, Sequence sequence) { @@ -111,6 +113,8 @@ * @throws IOException if an I/O exception occurs * @throws IllegalArgumentException if the file type is not supported by * this file writer + * @throws NullPointerException if {@code in} or {@code out} are + * {@code null} * @see #isFileTypeSupported(int, Sequence) * @see #getMidiFileTypes(Sequence) */ @@ -129,6 +133,8 @@ * @throws IOException if an I/O exception occurs * @throws IllegalArgumentException if the file type is not supported by * this file writer + * @throws NullPointerException if {@code in} or {@code out} are + * {@code null} * @see #isFileTypeSupported(int, Sequence) * @see #getMidiFileTypes(Sequence) */ diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/javax/sound/midi/spi/SoundbankReader.java --- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/SoundbankReader.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/SoundbankReader.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,6 +52,7 @@ * @throws InvalidMidiDataException if the URL does not point to valid MIDI * soundbank data recognized by this soundbank reader * @throws IOException if an I/O error occurs + * @throws NullPointerException if {@code url} is {@code null} */ public abstract Soundbank getSoundbank(URL url) throws InvalidMidiDataException, IOException; @@ -64,6 +65,7 @@ * @throws InvalidMidiDataException if the stream does not point to valid * MIDI soundbank data recognized by this soundbank reader * @throws IOException if an I/O error occurs + * @throws NullPointerException if {@code stream} is {@code null} */ public abstract Soundbank getSoundbank(InputStream stream) throws InvalidMidiDataException, IOException; @@ -76,6 +78,7 @@ * @throws InvalidMidiDataException if the file does not point to valid MIDI * soundbank data recognized by this soundbank reader * @throws IOException if an I/O error occurs + * @throws NullPointerException if {@code file} is {@code null} */ public abstract Soundbank getSoundbank(File file) throws InvalidMidiDataException, IOException; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/javax/swing/JComponent.java --- a/jdk/src/java.desktop/share/classes/javax/swing/JComponent.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/javax/swing/JComponent.java Wed Jul 05 21:10:34 2017 +0200 @@ -618,6 +618,7 @@ * @return the {@code ComponentUI} object that renders this component * @since 1.9 */ + @Transient public ComponentUI getUI() { return ui; } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java --- a/jdk/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java Wed Jul 05 21:10:34 2017 +0200 @@ -663,7 +663,9 @@ if(newFolder.exists()) { throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); } else { - newFolder.mkdirs(); + if(!newFolder.mkdirs()) { + throw new IOException(newFolder.getAbsolutePath()); + } } return newFolder; @@ -773,7 +775,9 @@ if(newFolder.exists()) { throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); } else { - newFolder.mkdirs(); + if(!newFolder.mkdirs()) { + throw new IOException(newFolder.getAbsolutePath()); + } } return newFolder; @@ -842,9 +846,10 @@ if(newFolder.exists()) { throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); } else { - newFolder.mkdirs(); + if(!newFolder.mkdirs()) { + throw new IOException(newFolder.getAbsolutePath()); + } } - return newFolder; } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/javax/swing/text/AbstractDocument.java --- a/jdk/src/java.desktop/share/classes/javax/swing/text/AbstractDocument.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/javax/swing/text/AbstractDocument.java Wed Jul 05 21:10:34 2017 +0200 @@ -36,6 +36,7 @@ import sun.font.BidiUtils; import sun.swing.SwingUtilities2; +import sun.swing.text.UndoableEditLockSupport; /** * An implementation of the document interface to serve as a @@ -275,6 +276,11 @@ * @see EventListenerList */ protected void fireUndoableEditUpdate(UndoableEditEvent e) { + if (e.getEdit() instanceof DefaultDocumentEvent) { + e = new UndoableEditEvent(e.getSource(), + new DefaultDocumentEventUndoableWrapper( + (DefaultDocumentEvent)e.getEdit())); + } // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying @@ -2952,6 +2958,88 @@ } + static class DefaultDocumentEventUndoableWrapper implements + UndoableEdit, UndoableEditLockSupport + { + final DefaultDocumentEvent dde; + public DefaultDocumentEventUndoableWrapper(DefaultDocumentEvent dde) { + this.dde = dde; + } + + @Override + public void undo() throws CannotUndoException { + dde.undo(); + } + + @Override + public boolean canUndo() { + return dde.canUndo(); + } + + @Override + public void redo() throws CannotRedoException { + dde.redo(); + } + + @Override + public boolean canRedo() { + return dde.canRedo(); + } + + @Override + public void die() { + dde.die(); + } + + @Override + public boolean addEdit(UndoableEdit anEdit) { + return dde.addEdit(anEdit); + } + + @Override + public boolean replaceEdit(UndoableEdit anEdit) { + return dde.replaceEdit(anEdit); + } + + @Override + public boolean isSignificant() { + return dde.isSignificant(); + } + + @Override + public String getPresentationName() { + return dde.getPresentationName(); + } + + @Override + public String getUndoPresentationName() { + return dde.getUndoPresentationName(); + } + + @Override + public String getRedoPresentationName() { + return dde.getRedoPresentationName(); + } + + /** + * {@inheritDoc} + * @since 1.9 + */ + @Override + public void lockEdit() { + ((AbstractDocument)dde.getDocument()).writeLock(); + } + + /** + * {@inheritDoc} + * @since 1.9 + */ + @Override + public void unlockEdit() { + ((AbstractDocument)dde.getDocument()).writeUnlock(); + } + } + /** * This event used when firing document changes while Undo/Redo * operations. It just wraps DefaultDocumentEvent and delegates diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/javax/swing/undo/UndoManager.java --- a/jdk/src/java.desktop/share/classes/javax/swing/undo/UndoManager.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/javax/swing/undo/UndoManager.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ import javax.swing.event.*; import javax.swing.UIManager; import java.util.*; +import sun.swing.text.UndoableEditLockSupport; /** * {@code UndoManager} manages a list of {@code UndoableEdits}, @@ -134,6 +135,11 @@ */ @SuppressWarnings("serial") // Same-version serialization only public class UndoManager extends CompoundEdit implements UndoableEditListener { + private enum Action { + UNDO, + REDO, + ANY + } int indexOfNextAdd; int limit; @@ -369,13 +375,8 @@ * @throws CannotRedoException if one of the edits throws * CannotRedoException */ - public synchronized void undoOrRedo() throws CannotRedoException, - CannotUndoException { - if (indexOfNextAdd == edits.size()) { - undo(); - } else { - redo(); - } + public void undoOrRedo() throws CannotRedoException, CannotUndoException { + tryUndoOrRedo(Action.ANY); } /** @@ -407,16 +408,8 @@ * @see #canUndo * @see #editToBeUndone */ - public synchronized void undo() throws CannotUndoException { - if (inProgress) { - UndoableEdit edit = editToBeUndone(); - if (edit == null) { - throw new CannotUndoException(); - } - undoTo(edit); - } else { - super.undo(); - } + public void undo() throws CannotUndoException { + tryUndoOrRedo(Action.UNDO); } /** @@ -452,16 +445,90 @@ * @see #canRedo * @see #editToBeRedone */ - public synchronized void redo() throws CannotRedoException { - if (inProgress) { - UndoableEdit edit = editToBeRedone(); - if (edit == null) { - throw new CannotRedoException(); + public void redo() throws CannotRedoException { + tryUndoOrRedo(Action.REDO); + } + + private void tryUndoOrRedo(Action action) { + UndoableEditLockSupport lockSupport = null; + boolean undo; + synchronized (this) { + if (action == Action.ANY) { + undo = indexOfNextAdd == edits.size(); + } else { + undo = action == Action.UNDO; + } + if (inProgress) { + UndoableEdit edit = undo ? editToBeUndone() : editToBeRedone(); + if (edit == null) { + throw undo ? new CannotUndoException() : + new CannotRedoException(); + } + lockSupport = getEditLockSupport(edit); + if (lockSupport == null) { + if (undo) { + undoTo(edit); + } else { + redoTo(edit); + } + return; + } + } else { + if (undo) { + super.undo(); + } else { + super.redo(); + } + return; } - redoTo(edit); - } else { - super.redo(); } + // the edit synchronization is required + while (true) { + lockSupport.lockEdit(); + UndoableEditLockSupport editLockSupport = null; + try { + synchronized (this) { + if (action == Action.ANY) { + undo = indexOfNextAdd == edits.size(); + } + if (inProgress) { + UndoableEdit edit = undo ? editToBeUndone() : + editToBeRedone(); + if (edit == null) { + throw undo ? new CannotUndoException() : + new CannotRedoException(); + } + editLockSupport = getEditLockSupport(edit); + if (editLockSupport == null || + editLockSupport == lockSupport) { + if (undo) { + undoTo(edit); + } else { + redoTo(edit); + } + return; + } + } else { + if (undo) { + super.undo(); + } else { + super.redo(); + } + return; + } + } + } finally { + if (lockSupport != null) { + lockSupport.unlockEdit(); + } + lockSupport = editLockSupport; + } + } + } + + private UndoableEditLockSupport getEditLockSupport(UndoableEdit anEdit) { + return anEdit instanceof UndoableEditLockSupport ? + (UndoableEditLockSupport)anEdit : null; } /** diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java --- a/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java Wed Jul 05 21:10:34 2017 +0200 @@ -70,6 +70,10 @@ { this.comp = comp; this.graphicsConfig = graphicsConfig; + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("Width (" + width + ")" + + " and height (" + height + ") cannot be <= 0"); + } this.width = width; this.height = height; this.forcedAccelSurfaceType = accType; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java --- a/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java Wed Jul 05 21:10:34 2017 +0200 @@ -445,13 +445,19 @@ } public void setGlyphPosition(int ix, Point2D pos) { + if (ix < 0 || ix > glyphs.length) { + throw new IndexOutOfBoundsException("ix = " + ix); + } + initPositions(); int ix2 = ix << 1; positions[ix2] = (float)pos.getX(); positions[ix2 + 1] = (float)pos.getY(); - clearCaches(ix); + if (ix < glyphs.length) { + clearCaches(ix); + } addFlags(FLAG_HAS_POSITION_ADJUSTMENTS); } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java --- a/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java Wed Jul 05 21:10:34 2017 +0200 @@ -176,6 +176,13 @@ private String localeFamilyName; private String localeFullName; + public TrueTypeFont(String platname, Object nativeNames, int fIndex, + boolean javaRasterizer) + throws FontFormatException + { + this(platname, nativeNames, fIndex, javaRasterizer, true); + } + /** * - does basic verification of the file * - reads the header table for this font (within a collection) @@ -186,14 +193,17 @@ * or fails verification, or there's no usable cmap */ public TrueTypeFont(String platname, Object nativeNames, int fIndex, - boolean javaRasterizer) + boolean javaRasterizer, boolean useFilePool) throws FontFormatException { super(platname, nativeNames); useJavaRasterizer = javaRasterizer; fontRank = Font2D.TTF_RANK; try { - verify(); + verify(useFilePool); init(fIndex); + if (!useFilePool) { + close(); + } } catch (Throwable t) { close(); if (t instanceof FontFormatException) { @@ -280,6 +290,10 @@ } + private synchronized FileChannel open() throws FontFormatException { + return open(true); + } + /* This is intended to be called, and the returned value used, * from within a block synchronized on this font object. * ie the channel returned may be nulled out at any time by "close()" @@ -287,7 +301,8 @@ * Deadlock warning: FontManager.addToPool(..) acquires a global lock, * which means nested locks may be in effect. */ - private synchronized FileChannel open() throws FontFormatException { + private synchronized FileChannel open(boolean usePool) + throws FontFormatException { if (disposerRecord.channel == null) { if (FontUtilities.isLogging()) { FontUtilities.getLogger().info("open TTF: " + platName); @@ -306,9 +321,11 @@ }); disposerRecord.channel = raf.getChannel(); fileSize = (int)disposerRecord.channel.size(); - FontManager fm = FontManagerFactory.getInstance(); - if (fm instanceof SunFontManager) { - ((SunFontManager) fm).addToPool(this); + if (usePool) { + FontManager fm = FontManagerFactory.getInstance(); + if (fm instanceof SunFontManager) { + ((SunFontManager) fm).addToPool(this); + } } } catch (NullPointerException e) { close(); @@ -492,8 +509,8 @@ } } - private void verify() throws FontFormatException { - open(); + private void verify(boolean usePool) throws FontFormatException { + open(usePool); } /* sizes, in bytes, of TT/TTC header records */ diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/ArrayCache.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ArrayCache.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ArrayCache.java Wed Jul 05 21:10:34 2017 +0200 @@ -166,18 +166,31 @@ * @return new array size */ public static int getNewSize(final int curSize, final int needSize) { + // check if needSize is negative or integer overflow: + if (needSize < 0) { + // hard overflow failure - we can't even accommodate + // new items without overflowing + throw new ArrayIndexOutOfBoundsException( + "array exceeds maximum capacity !"); + } + assert curSize >= 0; final int initial = (curSize & MASK_CLR_1); int size; if (initial > THRESHOLD_ARRAY_SIZE) { size = initial + (initial >> 1); // x(3/2) } else { - size = (initial) << 1; // x2 + size = (initial << 1); // x2 } // ensure the new size is >= needed size: if (size < needSize) { - // align to 4096: + // align to 4096 (may overflow): size = ((needSize >> 12) + 1) << 12; } + // check integer overflow: + if (size < 0) { + // resize to maximum capacity: + size = Integer.MAX_VALUE; + } return size; } @@ -188,26 +201,29 @@ * @return new array size */ public static long getNewLargeSize(final long curSize, final long needSize) { + // check if needSize is negative or integer overflow: + if ((needSize >> 31L) != 0L) { + // hard overflow failure - we can't even accommodate + // new items without overflowing + throw new ArrayIndexOutOfBoundsException( + "array exceeds maximum capacity !"); + } + assert curSize >= 0L; long size; if (curSize > THRESHOLD_HUGE_ARRAY_SIZE) { size = curSize + (curSize >> 2L); // x(5/4) } else if (curSize > THRESHOLD_LARGE_ARRAY_SIZE) { size = curSize + (curSize >> 1L); // x(3/2) } else { - size = curSize << 1L; // x2 + size = (curSize << 1L); // x2 } // ensure the new size is >= needed size: if (size < needSize) { // align to 4096: - size = ((needSize >> 12) + 1) << 12; + size = ((needSize >> 12L) + 1L) << 12L; } - if (size >= Integer.MAX_VALUE) { - if (curSize >= Integer.MAX_VALUE) { - // hard overflow failure - we can't even accommodate - // new items without overflowing - throw new ArrayIndexOutOfBoundsException( - "array exceeds maximum capacity !"); - } + // check integer overflow: + if (size > Integer.MAX_VALUE) { // resize to maximum capacity: size = Integer.MAX_VALUE; } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java Wed Jul 05 21:10:34 2017 +0200 @@ -74,7 +74,7 @@ void putDirtyArray(final byte[] array, final int length) { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } @@ -98,7 +98,7 @@ { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java Wed Jul 05 21:10:34 2017 +0200 @@ -75,7 +75,7 @@ void putDirtyArray(final float[] array, final int length) { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } @@ -99,7 +99,7 @@ { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatMath.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatMath.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatMath.java Wed Jul 05 21:10:34 2017 +0200 @@ -24,8 +24,8 @@ */ package sun.java2d.marlin; -import sun.misc.DoubleConsts; -import sun.misc.FloatConsts; +import jdk.internal.math.DoubleConsts; +import jdk.internal.math.FloatConsts; /** * Faster Math ceil / floor routines derived from StrictMath diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java Wed Jul 05 21:10:34 2017 +0200 @@ -74,7 +74,7 @@ void putDirtyArray(final int[] array, final int length) { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } @@ -98,7 +98,7 @@ { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java Wed Jul 05 21:10:34 2017 +0200 @@ -30,8 +30,8 @@ */ interface MarlinConst { // enable Logs (logger or stdout) - static final boolean enableLogs = false; - // enable Logger + static final boolean enableLogs = MarlinProperties.isLoggingEnabled(); + // use Logger instead of stdout static final boolean useLogger = enableLogs && MarlinProperties.isUseLogger(); // log new RendererContext @@ -47,9 +47,10 @@ static final boolean doStats = enableLogs && MarlinProperties.isDoStats(); // do monitors // disabled to reduce byte-code size a bit... - static final boolean doMonitors = enableLogs && false; // MarlinProperties.isDoMonitors(); + static final boolean doMonitors = false; +// static final boolean doMonitors = enableLogs && MarlinProperties.isDoMonitors(); // do checks - static final boolean doChecks = false; // MarlinProperties.isDoChecks(); + static final boolean doChecks = enableLogs && MarlinProperties.isDoChecks(); // do AA range checks: disable when algorithm / code is stable static final boolean DO_AA_RANGE_CHECK = false; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java Wed Jul 05 21:10:34 2017 +0200 @@ -136,6 +136,10 @@ // logging parameters + public static boolean isLoggingEnabled() { + return getBoolean("sun.java2d.renderer.log", "false"); + } + public static boolean isUseLogger() { return getBoolean("sun.java2d.renderer.useLogger", "false"); } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java Wed Jul 05 21:10:34 2017 +0200 @@ -27,12 +27,12 @@ public final class MarlinUtils { - // TODO: use sun.util.logging.PlatformLogger once in JDK9 - private static final java.util.logging.Logger log; + // Marlin logger + private static final sun.util.logging.PlatformLogger log; static { if (MarlinConst.useLogger) { - log = java.util.logging.Logger.getLogger("sun.java2d.marlin"); + log = sun.util.logging.PlatformLogger.getLogger("sun.java2d.marlin"); } else { log = null; } @@ -53,25 +53,11 @@ public static void logException(final String msg, final Throwable th) { if (MarlinConst.useLogger) { -// log.warning(msg, th); - log.log(java.util.logging.Level.WARNING, msg, th); + log.warning(msg, th); } else if (MarlinConst.enableLogs) { System.out.print("WARNING: "); System.out.println(msg); th.printStackTrace(System.err); } } - - // Returns the caller's class and method's name; best effort - // if cannot infer, return the logger's name. - static String getCallerInfo(String className) { - String sourceClassName = null; - String sourceMethodName = null; - - if (sourceClassName != null) { - return sourceClassName + " " + sourceMethodName; - } else { - return "unknown"; - } - } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java Wed Jul 05 21:10:34 2017 +0200 @@ -629,6 +629,13 @@ } if (edgeMinY != Float.POSITIVE_INFINITY) { + // if context is maked as DIRTY: + if (rdrCtx.dirty) { + // may happen if an exception if thrown in the pipeline processing: + // clear completely buckets arrays: + buckets_minY = 0; + buckets_maxY = boundsMaxY - boundsMinY; + } // clear used part if (edgeBuckets == edgeBuckets_initial) { // fill only used part diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java Wed Jul 05 21:10:34 2017 +0200 @@ -31,7 +31,6 @@ import java.util.concurrent.atomic.AtomicInteger; import static sun.java2d.marlin.ArrayCache.*; import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator; -import static sun.java2d.marlin.MarlinUtils.getCallerInfo; import static sun.java2d.marlin.MarlinUtils.logInfo; /** @@ -39,7 +38,6 @@ */ final class RendererContext implements MarlinConst { - private static final String className = RendererContext.class.getName(); // RendererContext creation counter private static final AtomicInteger contextCount = new AtomicInteger(1); // RendererContext statistics @@ -214,8 +212,7 @@ } if (doLogOverSize) { - logInfo("getDirtyByteArray[oversize]: length=\t" + length - + "\tfrom=\t" + getCallerInfo(className)); + logInfo("getDirtyByteArray[oversize]: length=\t" + length); } return new byte[length]; @@ -254,7 +251,7 @@ if (doLogWidenArray) { logInfo("widenDirtyByteArray[" + res.length + "]: usedSize=\t" + usedSize + "\tlength=\t" + length + "\tneeded length=\t" - + needSize + "\tfrom=\t" + getCallerInfo(className)); + + needSize); } return res; } @@ -275,8 +272,7 @@ } if (doLogOverSize) { - logInfo("getIntArray[oversize]: length=\t" + length + "\tfrom=\t" - + getCallerInfo(className)); + logInfo("getIntArray[oversize]: length=\t" + length); } return new int[length]; @@ -306,7 +302,7 @@ if (doLogWidenArray) { logInfo("widenIntArray[" + res.length + "]: usedSize=\t" + usedSize + "\tlength=\t" + length + "\tneeded length=\t" - + needSize + "\tfrom=\t" + getCallerInfo(className)); + + needSize); } return res; } @@ -338,8 +334,7 @@ } if (doLogOverSize) { - logInfo("getDirtyIntArray[oversize]: length=\t" + length - + "\tfrom=\t" + getCallerInfo(className)); + logInfo("getDirtyIntArray[oversize]: length=\t" + length); } return new int[length]; @@ -369,7 +364,7 @@ if (doLogWidenArray) { logInfo("widenDirtyIntArray[" + res.length + "]: usedSize=\t" + usedSize + "\tlength=\t" + length + "\tneeded length=\t" - + needSize + "\tfrom=\t" + getCallerInfo(className)); + + needSize); } return res; } @@ -399,8 +394,7 @@ } if (doLogOverSize) { - logInfo("getDirtyFloatArray[oversize]: length=\t" + length - + "\tfrom=\t" + getCallerInfo(className)); + logInfo("getDirtyFloatArray[oversize]: length=\t" + length); } return new float[length]; @@ -430,7 +424,7 @@ if (doLogWidenArray) { logInfo("widenDirtyFloatArray[" + res.length + "]: usedSize=\t" + usedSize + "\tlength=\t" + length + "\tneeded length=\t" - + needSize + "\tfrom=\t" + getCallerInfo(className)); + + needSize); } return res; } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java Wed Jul 05 21:10:34 2017 +0200 @@ -25,6 +25,8 @@ package sun.java2d.marlin; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentLinkedQueue; @@ -32,6 +34,7 @@ import sun.java2d.marlin.stats.Histogram; import sun.java2d.marlin.stats.Monitor; import sun.java2d.marlin.stats.StatLong; +import sun.awt.util.ThreadGroupUtils; /** * This class gathers global rendering statistics for debugging purposes only @@ -237,22 +240,33 @@ private RendererStats() { super(); - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - dump(); - } - }); + AccessController.doPrivileged( + (PrivilegedAction) () -> { + final Thread hook = new Thread( + ThreadGroupUtils.getRootThreadGroup(), + new Runnable() { + @Override + public void run() { + dump(); + } + }, + "MarlinStatsHook" + ); + hook.setContextClassLoader(null); + Runtime.getRuntime().addShutdownHook(hook); - if (useDumpThread) { - final Timer statTimer = new Timer("RendererStats"); - statTimer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - dump(); + if (useDumpThread) { + final Timer statTimer = new Timer("RendererStats"); + statTimer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + dump(); + } + }, statDump, statDump); } - }, statDump, statDump); - } + return null; + } + ); } void dump() { diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java Wed Jul 05 21:10:34 2017 +0200 @@ -1265,14 +1265,15 @@ } private void ensureSpace(final int n) { - if (end + n > curves.length) { + // use substraction to avoid integer overflow: + if (curves.length - end < n) { if (doStats) { RendererContext.stats.stat_array_stroker_polystack_curves .add(end + n); } curves = rdrCtx.widenDirtyFloatArray(curves, end, end + n); } - if (numCurves + 1 > curveTypes.length) { + if (curveTypes.length <= numCurves) { if (doStats) { RendererContext.stats.stat_array_stroker_polystack_curveTypes .add(numCurves + 1); diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/classes/sun/swing/text/UndoableEditLockSupport.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.desktop/share/classes/sun/swing/text/UndoableEditLockSupport.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.swing.text; + +import javax.swing.undo.UndoableEdit; + +/** + * UndoableEdit support for undo/redo actions synchronization + * @since 1.9 + */ +public interface UndoableEditLockSupport extends UndoableEdit { + /** + * lock the UndoableEdit for threadsafe undo/redo + */ + void lockEdit(); + + /** + * unlock the UndoableEdit + */ + void unlockEdit(); +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.cpp --- a/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.cpp Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.cpp Wed Jul 05 21:10:34 2017 +0200 @@ -67,12 +67,6 @@ }; -const void *FontInstanceAdapter::getFontTable(LETag tableTag) const -{ - size_t ignored = 0; - return getFontTable(tableTag, ignored); -} - static const LETag cacheMap[LAYOUTCACHE_ENTRIES] = { GPOS_TAG, GDEF_TAG, GSUB_TAG, MORT_TAG, MORX_TAG, KERN_TAG }; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.h --- a/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.h Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.h Wed Jul 05 21:10:34 2017 +0200 @@ -85,7 +85,6 @@ // tables are cached with the native font scaler data // only supports gsub, gpos, gdef, mort tables at present - virtual const void *getFontTable(LETag tableTag) const; virtual const void *getFontTable(LETag tableTag, size_t &len) const; virtual void *getKernPairs() const { diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c --- a/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c Wed Jul 05 21:10:34 2017 +0200 @@ -80,15 +80,18 @@ float scale = 1.0f/64.0f; unsigned int* glyphs; float* positions; + int initialCount, glyphArrayLen, posArrayLen, maxGlyphs, storeadv; + unsigned int* indices; + jarray glyphArray, posArray, inxArray; if (!init_JNI_IDs(env)) { return 0; } - int initialCount = (*env)->GetIntField(env, gvdata, gvdCountFID); - jarray glyphArray = + initialCount = (*env)->GetIntField(env, gvdata, gvdCountFID); + glyphArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdGlyphsFID); - jarray posArray = + posArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdPositionsFID); if (glyphArray == NULL || posArray == NULL) @@ -101,9 +104,9 @@ // and re-invokes layout. I suppose this is expected to be rare // because at least in a single threaded case there should be // re-use of the same container, but it is a little wasteful/distateful. - int glyphArrayLen = (*env)->GetArrayLength(env, glyphArray); - int posArrayLen = (*env)->GetArrayLength(env, posArray); - int maxGlyphs = glyphCount + initialCount; + glyphArrayLen = (*env)->GetArrayLength(env, glyphArray); + posArrayLen = (*env)->GetArrayLength(env, posArray); + maxGlyphs = glyphCount + initialCount; if ((maxGlyphs > glyphArrayLen) || (maxGlyphs * 2 + 2 > posArrayLen)) { @@ -125,7 +128,7 @@ x += glyphPos[i].x_advance * scale; y += glyphPos[i].y_advance * scale; } - int storeadv = initialCount+glyphCount; + storeadv = initialCount+glyphCount; // The final slot in the positions array is important // because when the GlyphVector is created from this // data it determines the overall advance of the glyphvector @@ -138,11 +141,10 @@ (*env)->ReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0); (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, 0); putFloat(env, startPt,positions[(storeadv*2)],positions[(storeadv*2)+1] ); - jarray inxArray = + inxArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdIndicesFID); - unsigned int* indices = + indices = (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, inxArray, NULL); - int prevCluster = -1; for (i = 0; i < glyphCount; i++) { int cluster = glyphInfo[i].cluster; if (direction == HB_DIRECTION_LTR) { diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c --- a/jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c Wed Jul 05 21:10:34 2017 +0200 @@ -258,7 +258,7 @@ scalerInfo->fontData, scalerInfo->fontDataLength); if (bBuffer != NULL) { - (*env)->CallObjectMethod(env, font2D, + (*env)->CallVoidMethod(env, font2D, sunFontIDs.readFileMID, bBuffer); error = FT_New_Memory_Face(scalerInfo->library, diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/native/libfontmanager/layout/LEFontInstance.h --- a/jdk/src/java.desktop/share/native/libfontmanager/layout/LEFontInstance.h Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/native/libfontmanager/layout/LEFontInstance.h Wed Jul 05 21:10:34 2017 +0200 @@ -181,28 +181,6 @@ * * Subclasses which represent composite fonts should always return NULL. * - * Note that implementing this function does not allow for range checking. - * Subclasses that desire the safety of range checking must implement the - * variation which has a length parameter. - * - * @param tableTag - the four byte table tag. (e.g. 'cmap') - * - * @return the address of the table in memory, or NULL - * if the table doesn't exist. - * - * @stable ICU 2.8 - */ - virtual const void *getFontTable(LETag tableTag) const = 0; - - /** - * This method reads a table from the font. Note that in general, - * it only makes sense to call this method on an LEFontInstance - * which represents a physical font - i.e. one which has been returned by - * getSubFont(). This is because each subfont in a composite font - * will have different tables, and there's no way to know which subfont to access. - * - * Subclasses which represent composite fonts should always return NULL. - * * This version sets a length, for range checking. * Note that range checking can only be accomplished if this function is * implemented in subclasses. @@ -213,7 +191,7 @@ * if the table doesn't exist. * @internal */ - virtual const void* getFontTable(LETag tableTag, size_t &length) const { length=-1; return getFontTable(tableTag); } /* -1 = unknown length */ + virtual const void* getFontTable(LETag tableTag, size_t &length) const = 0; virtual void *getKernPairs() const = 0; virtual void setKernPairs(void *pairs) const = 0; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c --- a/jdk/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c Wed Jul 05 21:10:34 2017 +0200 @@ -1610,6 +1610,7 @@ int ret; int h_samp0, h_samp1, h_samp2; int v_samp0, v_samp1, v_samp2; + int cid0, cid1, cid2; jboolean retval = JNI_FALSE; imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr); j_decompress_ptr cinfo; @@ -1711,17 +1712,15 @@ } } else if (!cinfo->saw_JFIF_marker && !IS_EXIF(cinfo)) { /* - * IJG assumes all unidentified 3-channels are YCbCr. - * We assume that only if the second two channels are - * subsampled (either horizontally or vertically). If not, - * we assume RGB. - * - * 4776576: Some digital cameras output YCbCr JPEG images - * that do not contain a JFIF APP0 marker but are only - * vertically subsampled (no horizontal subsampling). - * We should only assume this is RGB data if the subsampling - * factors for the second two channels are the same as the - * first (check both horizontal and vertical factors). + * In the absence of certain markers, IJG has interpreted + * component id's of [1,2,3] as meaning YCbCr.We follow that + * interpretation, which is additionally described in the Image + * I/O JPEG metadata spec.If that condition is not met here the + * next step will be to examine the subsampling factors, if + * there is any difference in subsampling factors we also assume + * YCbCr, only if both horizontal and vertical subsampling + * is same we assume JPEG color space as RGB. + * This is also described in the Image I/O JPEG metadata spec. */ h_samp0 = cinfo->comp_info[0].h_samp_factor; h_samp1 = cinfo->comp_info[1].h_samp_factor; @@ -1731,8 +1730,13 @@ v_samp1 = cinfo->comp_info[1].v_samp_factor; v_samp2 = cinfo->comp_info[2].v_samp_factor; - if ((h_samp1 == h_samp0) && (h_samp2 == h_samp0) && - (v_samp1 == v_samp0) && (v_samp2 == v_samp0)) + cid0 = cinfo->comp_info[0].component_id; + cid1 = cinfo->comp_info[1].component_id; + cid2 = cinfo->comp_info[2].component_id; + + if ((!(cid0 == 1 && cid1 == 2 && cid2 == 3)) && + ((h_samp1 == h_samp0) && (h_samp2 == h_samp0) && + (v_samp1 == v_samp0) && (v_samp2 == v_samp0))) { cinfo->jpeg_color_space = JCS_RGB; /* output is already RGB, so it stays the same */ diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java Wed Jul 05 21:10:34 2017 +0200 @@ -413,6 +413,7 @@ void addListeners() { canvas.addMouseListener(eventProxy); canvas.addMouseMotionListener(eventProxy); + eframe.addMouseListener(eventProxy); } long getWindow() { diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java --- a/jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java Wed Jul 05 21:10:34 2017 +0200 @@ -926,7 +926,10 @@ return copyflavors; } } - return null; + DocFlavor[] flavor = new DocFlavor[2]; + flavor[0] = DocFlavor.SERVICE_FORMATTED.PAGEABLE; + flavor[1] = DocFlavor.SERVICE_FORMATTED.PRINTABLE; + return flavor; } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/unix/native/common/java2d/x11/X11SurfaceData.c --- a/jdk/src/java.desktop/unix/native/common/java2d/x11/X11SurfaceData.c Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/unix/native/common/java2d/x11/X11SurfaceData.c Wed Jul 05 21:10:34 2017 +0200 @@ -438,6 +438,15 @@ xsdo->drawable = drawable; xsdo->isPixmap = JNI_FALSE; } else { + /* + * width , height must be nonzero otherwise XCreatePixmap + * generates BadValue in error_handler + */ + if (width <= 0 || height <= 0) { + JNU_ThrowOutOfMemoryError(env, + "Can't create offscreen surface"); + return JNI_FALSE; + } xsdo->isPixmap = JNI_TRUE; /* REMIND: workaround for bug 4420220 on pgx32 boards: don't use DGA with pixmaps unless USE_DGA_PIXMAPS is set. diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java --- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java Wed Jul 05 21:10:34 2017 +0200 @@ -61,7 +61,7 @@ * enumerate (allow direct use) of EUDC fonts. */ eudcFont = new TrueTypeFont(eudcFile, null, 0, - true); + true, false); } catch (FontFormatException e) { } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/AWTEventMonitor.java --- a/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/AWTEventMonitor.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/AWTEventMonitor.java Wed Jul 05 21:10:34 2017 +0200 @@ -48,8 +48,6 @@ @jdk.Exported public class AWTEventMonitor { - static private boolean runningOnJDK1_4 = false; - /** * The current component with keyboard focus. * @@ -638,15 +636,9 @@ * @see AWTEventMonitor */ public AWTEventsListener() { - String version = System.getProperty("java.version"); - if (version != null) { - runningOnJDK1_4 = (version.compareTo("1.4") >= 0); - } initializeIntrospection(); installListeners(); - if (runningOnJDK1_4) { - MenuSelectionManager.defaultManager().addChangeListener(this); - } + MenuSelectionManager.defaultManager().addChangeListener(this); EventQueueMonitor.addTopLevelWindowListener(this); } @@ -848,15 +840,7 @@ case EventID.FOCUS: c.removeFocusListener(this); c.addFocusListener(this); - - if (runningOnJDK1_4) { - processFocusGained(); - - } else { // not runningOnJDK1_4 - if ((c != componentWithFocus_private) && c.hasFocus()) { - componentWithFocus_private = c; - } - } + processFocusGained(); break; case EventID.ITEM: diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java --- a/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java Wed Jul 05 21:10:34 2017 +0200 @@ -140,10 +140,6 @@ // initialize AccessibleRole map initAccessibleRoleMap(); - // determine which version of the JDK is running - String version = getJavaVersionProperty(); - debugString("JDK version = "+version); - // initialize the methods that map HWNDs and Java top-level // windows initHWNDcalls(); @@ -215,9 +211,7 @@ } catch (Exception e) {} /* - Build the extendedVirtualNameSearchRoles array list. I chose this method - because some of the Accessible Roles that need to be added to it are not - available in all versions of the J2SE that we want to support. + Build the extendedVirtualNameSearchRoles array list. */ extendedVirtualNameSearchRoles.add (AccessibleRole.COMBO_BOX); try { @@ -5919,7 +5913,7 @@ */ AccessibleRole.UNKNOWN, - // These roles are only available in JDK 1.4 + // These roles are available since JDK 1.4 /** * A STATUS_BAR is an simple component that can contain diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nio.zipfs; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * Adds aliasing to ZipFileSystem to support multi-release jar files. An alias map + * is created by {@link JarFileSystem#createVersionedLinks(int)}. The map is then + * consulted when an entry is looked up in {@link JarFileSystem#getEntry(byte[])} + * to determine if the entry has a corresponding versioned entry. If so, the + * versioned entry is returned. + * + * @author Steve Drach + */ + +class JarFileSystem extends ZipFileSystem { + private Function lookup; + + @Override + Entry getEntry(byte[] path) throws IOException { + // check for an alias to a versioned entry + byte[] versionedPath = lookup.apply(path); + return versionedPath == null ? super.getEntry(path) : super.getEntry(versionedPath); + } + + JarFileSystem(ZipFileSystemProvider provider, Path zfpath, Map env) + throws IOException { + super(provider, zfpath, env); + lookup = path -> path; // lookup needs to be set before isMultiReleaseJar is called + // because it eventually calls getEntry + if (isMultiReleaseJar()) { + int version; + Object o = env.get("multi-release"); + if (o instanceof String) { + String s = (String)o; + if (s.equals("runtime")) { + version = sun.misc.Version.jdkMajorVersion(); // fixme waiting for jdk.util.Version + } else { + version = Integer.parseInt(s); + } + } else if (o instanceof Integer) { + version = (Integer)o; + } else if (false /*o instanceof Version*/) { // fixme waiting for jdk.util.Version +// version = ((Version)o).major(); + } else { + throw new IllegalArgumentException("env parameter must be String, Integer, " + + "or Version"); + } + lookup = createVersionedLinks(version < 0 ? 0 : version); + setReadOnly(); + } + } + + private boolean isMultiReleaseJar() { + try (InputStream is = newInputStream(getBytes("META-INF/MANIFEST.MF"))) { + return (new Manifest(is)).getMainAttributes() + .containsKey(new Attributes.Name("Multi-Release")); + // fixme change line above after JarFile integration to contain Attributes.Name.MULTI_RELEASE + } catch (IOException x) { + return false; + } + } + + /** + * create a map of aliases for versioned entries, for example: + * version/PackagePrivate.class -> META-INF/versions/9/version/PackagePrivate.class + * version/PackagePrivate.java -> META-INF/versions/9/version/PackagePrivate.java + * version/Version.class -> META-INF/versions/10/version/Version.class + * version/Version.java -> META-INF/versions/10/version/Version.java + * + * then wrap the map in a function that getEntry can use to override root + * entry lookup for entries that have corresponding versioned entries + */ + private Function createVersionedLinks(int version) { + HashMap aliasMap = new HashMap<>(); + getVersionMap(version, getInode(getBytes("META-INF/versions"))).values() + .forEach(versionNode -> { // for each META-INF/versions/{n} directory + // put all the leaf inodes, i.e. entries, into the alias map + // possibly shadowing lower versioned entries + walk(versionNode, entryNode -> { + byte[] rootName = getRootName(versionNode, entryNode); + if (rootName != null) { + IndexNode rootNode = getInode(rootName); + if (rootNode == null) { // no matching root node, make a virtual one + rootNode = IndexNode.keyOf(rootName); + } + aliasMap.put(rootNode, entryNode.name); + } + }); + }); + return path -> aliasMap.get(IndexNode.keyOf(path)); + } + + /** + * create a sorted version map of version -> inode, for inodes <= max version + * 9 -> META-INF/versions/9 + * 10 -> META-INF/versions/10 + */ + private TreeMap getVersionMap(int version, IndexNode metaInfVersions) { + TreeMap map = new TreeMap<>(); + IndexNode child = metaInfVersions.child; + while (child != null) { + Integer key = getVersion(child.name, metaInfVersions.name.length); + if (key != null && key <= version) { + map.put(key, child); + } + child = child.sibling; + } + return map; + } + + /** + * extract the integer version number -- META-INF/versions/9 returns 9 + */ + private Integer getVersion(byte[] name, int offset) { + try { + return Integer.parseInt(getString(Arrays.copyOfRange(name, offset, name.length-1))); + } catch (NumberFormatException x) { + // ignore this even though it might indicate issues with the JAR structure + return null; + } + } + + /** + * walk the IndexNode tree processing all leaf nodes + */ + private void walk(IndexNode inode, Consumer process) { + if (inode == null) return; + if (inode.isDir()) { + walk(inode.child, process); + } else { + process.accept(inode); + walk(inode.sibling, process); + } + } + + /** + * extract the root name from a versioned entry name + * given inode for META-INF/versions/9/foo/bar.class + * and prefix META-INF/versions/9/ + * returns foo/bar.class + */ + private byte[] getRootName(IndexNode prefix, IndexNode inode) { + int offset = prefix.name.length; + byte[] fullName = inode.name; + return Arrays.copyOfRange(fullName, offset, fullName.length); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java --- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java Wed Jul 05 21:10:34 2017 +0200 @@ -155,6 +155,10 @@ throw new ReadOnlyFileSystemException(); } + void setReadOnly() { + this.readOnly = true; + } + @Override public Iterable getRootDirectories() { ArrayList pathArr = new ArrayList<>(); @@ -320,7 +324,7 @@ beginRead(); try { ensureOpen(); - e = getEntry0(path); + e = getEntry(path); if (e == null) { IndexNode inode = getInode(path); if (inode == null) @@ -342,7 +346,7 @@ beginWrite(); try { ensureOpen(); - Entry e = getEntry0(path); // ensureOpen checked + Entry e = getEntry(path); // ensureOpen checked if (e == null) throw new NoSuchFileException(getString(path)); if (e.type == Entry.CEN) @@ -445,7 +449,7 @@ beginWrite(); try { ensureOpen(); - Entry eSrc = getEntry0(src); // ensureOpen checked + Entry eSrc = getEntry(src); // ensureOpen checked if (eSrc == null) throw new NoSuchFileException(getString(src)); if (eSrc.isDir()) { // spec says to create dst dir @@ -460,7 +464,7 @@ else if (opt == COPY_ATTRIBUTES) hasCopyAttrs = true; } - Entry eDst = getEntry0(dst); + Entry eDst = getEntry(dst); if (eDst != null) { if (!hasReplace) throw new FileAlreadyExistsException(getString(dst)); @@ -521,7 +525,7 @@ beginRead(); // only need a readlock, the "update()" will try { // try to obtain a writelock when the os is ensureOpen(); // being closed. - Entry e = getEntry0(path); + Entry e = getEntry(path); if (e != null) { if (e.isDir() || hasCreateNew) throw new FileAlreadyExistsException(getString(path)); @@ -550,7 +554,7 @@ beginRead(); try { ensureOpen(); - Entry e = getEntry0(path); + Entry e = getEntry(path); if (e == null) throw new NoSuchFileException(getString(path)); if (e.isDir()) @@ -592,7 +596,7 @@ newOutputStream(path, options.toArray(new OpenOption[0]))); long leftover = 0; if (options.contains(StandardOpenOption.APPEND)) { - Entry e = getEntry0(path); + Entry e = getEntry(path); if (e != null && e.size >= 0) leftover = e.size; } @@ -644,7 +648,7 @@ beginRead(); try { ensureOpen(); - Entry e = getEntry0(path); + Entry e = getEntry(path); if (e == null || e.isDir()) throw new NoSuchFileException(getString(path)); final ReadableByteChannel rbc = @@ -714,7 +718,7 @@ beginRead(); try { ensureOpen(); - Entry e = getEntry0(path); + Entry e = getEntry(path); if (forWrite) { checkWritable(); if (e == null) { @@ -855,7 +859,7 @@ private Path getTempPathForEntry(byte[] path) throws IOException { Path tmpPath = createTempFileInSameDirectoryAs(zfpath); if (path != null) { - Entry e = getEntry0(path); + Entry e = getEntry(path); if (e != null) { try (InputStream is = newInputStream(path)) { Files.copy(is, tmpPath, REPLACE_EXISTING); @@ -939,7 +943,7 @@ private long getDataPos(Entry e) throws IOException { if (e.locoff == -1) { - Entry e2 = getEntry0(e.name); + Entry e2 = getEntry(e.name); if (e2 == null) throw new ZipException("invalid loc for entry <" + e.name + ">"); e.locoff = e2.locoff; @@ -1325,7 +1329,7 @@ //System.out.printf("->sync(%s) done!%n", toString()); } - private IndexNode getInode(byte[] path) { + IndexNode getInode(byte[] path) { if (path == null) throw new NullPointerException("path"); IndexNode key = IndexNode.keyOf(path); @@ -1340,7 +1344,7 @@ return inode; } - private Entry getEntry0(byte[] path) throws IOException { + Entry getEntry(byte[] path) throws IOException { IndexNode inode = getInode(path); if (inode instanceof Entry) return (Entry)inode; @@ -2096,7 +2100,7 @@ pos += (LOCHDR + nlen + elen); if ((flag & FLAG_DATADESCR) != 0) { // Data Descriptor - Entry e = zipfs.getEntry0(name); // get the size/csize from cen + Entry e = zipfs.getEntry(name); // get the size/csize from cen if (e == null) throw new ZipException("loc: name not found in cen"); size = e.size; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystemProvider.java --- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystemProvider.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystemProvider.java Wed Jul 05 21:10:34 2017 +0200 @@ -100,7 +100,11 @@ } ZipFileSystem zipfs = null; try { - zipfs = new ZipFileSystem(this, path, env); + if (env.containsKey("multi-release")) { + zipfs = new JarFileSystem(this, path, env); + } else { + zipfs = new ZipFileSystem(this, path, env); + } } catch (ZipException ze) { String pname = path.toString(); if (pname.endsWith(".zip") || pname.endsWith(".jar")) @@ -124,8 +128,14 @@ throw new UnsupportedOperationException(); } ensureFile(path); - try { - return new ZipFileSystem(this, path, env); + try { + ZipFileSystem zipfs; + if (env.containsKey("multi-release")) { + zipfs = new JarFileSystem(this, path, env); + } else { + zipfs = new ZipFileSystem(this, path, env); + } + return zipfs; } catch (ZipException ze) { String pname = path.toString(); if (pname.endsWith(".zip") || pname.endsWith(".jar")) diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/ProblemList.txt --- a/jdk/test/ProblemList.txt Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/ProblemList.txt Wed Jul 05 21:10:34 2017 +0200 @@ -310,8 +310,6 @@ # jdk_imageio -javax/imageio/plugins/shared/WriteAfterAbort.java generic-all - ############################################################################ # jdk_swing diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/List/SetFontTest/SetFontTest.html --- a/jdk/test/java/awt/List/SetFontTest/SetFontTest.html Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/List/SetFontTest/SetFontTest.html Wed Jul 05 21:10:34 2017 +0200 @@ -38,6 +38,6 @@

    See the dialog box (usually in upper left corner) for instructions

    - + diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.html --- a/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.html Wed Dec 23 15:41:50 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. - * 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. - */ - - - - - - - -

    bug 6176814
    Bug ID: 6176814

    - -

    This is an AUTOMATIC test, simply wait for completion

    - - - - diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.java --- a/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2015 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,91 +22,149 @@ */ /* - test - @bug 6176814 - @summary Metalworks frame maximizes after the move - @author Andrei.Dmitriev area=Event - @run applet MaximizedFrameTest.html -*/ + @test + @bug 6176814 8132766 + @summary Metalworks frame maximizes after the move + @run main MaximizedFrameTest + */ -import java.applet.Applet; -import javax.swing.*; -import java.awt.event.*; -import java.awt.*; +import java.awt.AWTException; +import java.awt.Component; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JFrame; +import javax.swing.JLayeredPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; -public class MaximizedFrameTest extends Applet -{ - final int ITERATIONS_COUNT = 20; - Robot robot; - Point framePosition; - Point newFrameLocation; - JFrame frame; - Rectangle gcBounds; - public static Object LOCK = new Object(); +public class MaximizedFrameTest { + + final static int ITERATIONS_COUNT = 5; + private static JFrame frame; + private static Point tempMousePosition; + private static Component titleComponent; - public void init() - { - String[] instructions = - { - "This is an AUTOMATIC test", - "simply wait until it is done" - }; + public void init() { JFrame.setDefaultLookAndFeelDecorated(true); + + try { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + } catch (ClassNotFoundException | InstantiationException | + IllegalAccessException | UnsupportedLookAndFeelException ex) { + throw new RuntimeException("Test Failed. MetalLookAndFeel not set " + + "for frame"); + } + frame = new JFrame("JFrame Maximization Test"); frame.pack(); frame.setSize(450, 260); - }//End init() + frame.setVisible(true); + } + + public void getTitleComponent() throws Exception { + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + JLayeredPane lPane = frame.getLayeredPane(); + boolean titleFound = false; + + for (int j = 0; j < lPane.getComponentsInLayer( + JLayeredPane.FRAME_CONTENT_LAYER.intValue()).length; j++) { + + titleComponent = lPane.getComponentsInLayer( + JLayeredPane.FRAME_CONTENT_LAYER.intValue())[j]; - public void start () - { - frame.setVisible(true); - validate(); - JLayeredPane lPane = frame.getLayeredPane(); - // System.out.println("JFrame's LayeredPane " + lPane ); - Component titleComponent = null; - boolean titleFound = false; - for (int j=0; j < lPane.getComponentsInLayer(JLayeredPane.FRAME_CONTENT_LAYER.intValue()).length; j++){ - titleComponent = lPane.getComponentsInLayer(JLayeredPane.FRAME_CONTENT_LAYER.intValue())[j]; - if (titleComponent.getClass().getName().equals("javax.swing.plaf.metal.MetalTitlePane")){ - titleFound = true; - break; + if (titleComponent.getClass().getName().equals( + "javax.swing.plaf.metal.MetalTitlePane")) { + + titleFound = true; + break; + } + } + + if (!titleFound) { + try { + dispose(); + } catch (Exception ex) { + Logger.getLogger(MaximizedFrameTest.class.getName()) + .log(Level.SEVERE, null, ex); + } + throw new RuntimeException("Test Failed. Unable to " + + "determine title component"); + } } - } - if ( !titleFound ){ - throw new RuntimeException("Test Failed. Unable to determine title's size."); - } - //-------------------------------- - // it is sufficient to get maximized Frame only once. - Point tempMousePosition; - framePosition = frame.getLocationOnScreen(); + }); + } + + public void doMaximizeFrameTest() throws Exception { + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + Point framePosition = frame.getLocationOnScreen(); + + tempMousePosition = new Point(framePosition.x + + frame.getWidth() / 2, framePosition.y + + titleComponent.getHeight() / 2); + } + }); + try { - robot = new Robot(); - tempMousePosition = new Point(framePosition.x + - frame.getWidth()/2, - framePosition.y + - titleComponent.getHeight()/2); + Robot robot = new Robot(); robot.mouseMove(tempMousePosition.x, tempMousePosition.y); - for (int iteration=0; iteration < ITERATIONS_COUNT; iteration++){ + robot.waitForIdle(); + + for (int iteration = 0; iteration < ITERATIONS_COUNT; iteration++) { robot.mousePress(InputEvent.BUTTON1_MASK); - gcBounds = - GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0].getConfigurations()[0].getBounds(); - //Moving a mouse pointer less than a few pixels - //leads to rising a double click event. - //We have to use exceeded the AWT_MULTICLICK_SMUDGE - //const value (which is 4 by default on GNOME) to test that. + robot.waitForIdle(); + + // Moving a mouse pointer less than a few pixels + // leads to rising a double click event. + // We have to use exceeded the AWT_MULTICLICK_SMUDGE + // const value (which is 4 by default on GNOME) to test that. tempMousePosition.x += 5; robot.mouseMove(tempMousePosition.x, tempMousePosition.y); - robot.delay(70); + robot.waitForIdle(); robot.mouseRelease(InputEvent.BUTTON1_MASK); - if ( frame.getExtendedState() != 0 ){ - throw new RuntimeException ("Test failed. JFrame was maximized. ExtendedState is : "+frame.getExtendedState()); + robot.waitForIdle(); + + if (frame.getExtendedState() != 0) { + dispose(); + throw new RuntimeException("Test failed. JFrame was " + + "maximized. ExtendedState is : " + + frame.getExtendedState()); } - robot.delay(500); - } //for iteration + } + } catch (AWTException e) { + dispose(); + throw new RuntimeException("Test Failed. AWTException thrown."); + } + System.out.println("Test passed."); + } - }catch(AWTException e) { - throw new RuntimeException("Test Failed. AWTException thrown."); + private void dispose() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if (null != frame) { + frame.dispose(); + } } - System.out.println("Test passed."); - }// start() -}// class + }); + } + + public static void main(String[] args) throws Exception { + + MaximizedFrameTest maximizedFrameTest = new MaximizedFrameTest(); + maximizedFrameTest.init(); + maximizedFrameTest.getTitleComponent(); + maximizedFrameTest.doMaximizeFrameTest(); + maximizedFrameTest.dispose(); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TextArea/TextAreaEditing/TextAreaEditing.java --- a/jdk/test/java/awt/TextArea/TextAreaEditing/TextAreaEditing.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TextArea/TextAreaEditing/TextAreaEditing.java Wed Jul 05 21:10:34 2017 +0200 @@ -23,16 +23,23 @@ /* @test - @bug 8040322 + @bug 8040322 8060137 + @library ../../regtesthelpers + @build Util @summary Test TextArea APIs replaceRange, insert, append & setText @run main TextAreaEditing */ import java.awt.Frame; +import java.awt.Robot; import java.awt.TextArea; +import java.awt.AWTException; +import java.awt.event.KeyEvent; +import test.java.awt.regtesthelpers.Util; public class TextAreaEditing { + final static Robot robot = Util.createRobot(); private int testFailCount; private boolean isTestFail; private StringBuilder testFailMessage; @@ -61,6 +68,7 @@ textArea.testReplaceRange(); textArea.testInsert(); textArea.testAppend(); + textArea.testSetText(); textArea.checkFailures(); textArea.dispose(); } @@ -119,6 +127,24 @@ checkTest(""); } + private void testSetText() { + textArea.setText(null); + textArea.requestFocus(); + Util.clickOnComp(textArea, robot); + Util.waitForIdle(robot); + robot.keyPress(KeyEvent.VK_A); + robot.delay(5); + robot.keyRelease(KeyEvent.VK_A); + Util.waitForIdle(robot); + textArea.setText(null); + checkTest(""); + textArea.setText("CaseSensitive"); + checkTest("CaseSensitive"); + textArea.setText("caseSensitive"); + checkTest("caseSensitive"); + + } + private void checkTest(String str) { if (str != null && !str.equals(textArea.getText())) { testFailMessage.append("TestFail line : "); diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TextField/EOLTest/EOLTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/awt/TextField/EOLTest/EOLTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8055197 7186036 + @summary TextField should replace EOL character with space character + @run main EOLTest + */ + +import java.awt.Frame; +import java.awt.TextField; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; + +public class EOLTest { + + private Frame mainFrame; + private TextField textField; + private String testStrEOL; + private boolean isTestFail; + private int testFailCount; + StringBuilder testFailMessage; + private String expectedString = "Row1 Row2 Row3"; + + public EOLTest() { + mainFrame = new Frame(); + mainFrame.setSize(200, 200); + mainFrame.setVisible(true); + testFailMessage = new StringBuilder(); + testStrEOL = "Row1" + System.lineSeparator() + "Row2\nRow3"; + } + + private void testConstructor1() { + textField = new TextField(testStrEOL); + textField.setSize(200, 100); + mainFrame.add(textField); + checkTest(); + mainFrame.remove(textField); + } + + private void testConstructor2() { + textField = new TextField(30); + textField.setSize(200, 100); + mainFrame.add(textField); + textField.setText(testStrEOL); + checkTest(); + mainFrame.remove(textField); + } + + private void testConstructor3() { + textField = new TextField(testStrEOL, 30); + textField.setSize(200, 100); + mainFrame.add(textField); + checkTest(); + mainFrame.remove(textField); + } + + private void testSetText() { + textField = new TextField(); + textField.setSize(200, 100); + textField.setText(testStrEOL); + mainFrame.add(textField); + checkTest(); + mainFrame.remove(textField); + } + + private void testDeserialization() { + TextField textFieldToSerialize = new TextField(testStrEOL); + textFieldToSerialize.setSize(200, 100); + mainFrame.add(textFieldToSerialize); + try { + // Serialize TextField object "textFieldToSerialize". + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutput outStream = new ObjectOutputStream(baos); + outStream.writeObject(textFieldToSerialize); + + // Search the text variable data through serialized object stream. + byte[] streamedBytes = baos.toByteArray(); + int foundLoc = 0; + for (int i = 0; i < streamedBytes.length; ++i) { + if (streamedBytes[i] == expectedString.charAt(0)) { + foundLoc = i; + int j = 1; + for (; j < expectedString.length(); ++j) { + if (streamedBytes[i+j] != expectedString.charAt(j)) { + break; + } + } + if (j == expectedString.length()) { + break; + } + } + foundLoc = -1; + } + + if (foundLoc == -1) { + // Could not find text data in serialized object stream. + throw new Exception("Could not find text data in serialized " + + "object stream."); + } + // Replace space character from serialized stream with + // EOL character for testing de-serialization. + String EOLChar = System.lineSeparator(); + String newExpectedString = ""; + for (int i = foundLoc, j = 0; j < expectedString.length(); ++i, ++j) { + newExpectedString += (char)(streamedBytes[i]); + if (streamedBytes[i] == ' ') { + int k = 0; + for (; k < EOLChar.length(); ++k) { + streamedBytes[i + k] = (byte) EOLChar.charAt(k); + } + i += k-1; + j += k-1; + } + } + // New line character varies with platform, + // ex. For windows '\r\n', for linux '\n'. + // While replacing space from serialized object stream, the length + // of EOL character will affect the expected string as well. + expectedString = newExpectedString; + + // De-serialize TextField object stream. + ByteArrayInputStream bais = new ByteArrayInputStream(streamedBytes); + ObjectInput inStream = new ObjectInputStream(bais); + textField = (TextField) inStream.readObject(); + } catch (Exception ex) { + // Serialization or De-serialization failed. + // Create textField with empty string to show failure. + ex.printStackTrace(); + textField = new TextField(); + } + + checkTest(); + mainFrame.remove(textFieldToSerialize); + } + + private void checkTest() { + if (!textField.getText().equals(expectedString)) { + testFailMessage.append("TestFail line : "); + testFailMessage.append(Thread.currentThread().getStackTrace()[2]. + getLineNumber()); + testFailMessage.append(" TextField.getText() : \""); + testFailMessage.append(textField.getText()); + testFailMessage.append("\" does not match expected string : \""); + testFailMessage.append(expectedString).append("\""); + testFailMessage.append(System.getProperty("line.separator")); + testFailCount++; + isTestFail = true; + } + } + + private void checkFailures() { + if (isTestFail) { + testFailMessage.insert(0, "Test Fail count : " + testFailCount + + System.getProperty("line.separator")); + dispose(); + throw new RuntimeException(testFailMessage.toString()); + } + } + + private void dispose() { + if (mainFrame != null) { + mainFrame.dispose(); + } + } + + public static void main(String[] args) { + EOLTest testEOL = new EOLTest(); + testEOL.testConstructor1(); + testEOL.testConstructor2(); + testEOL.testConstructor3(); + testEOL.testSetText(); + testEOL.testDeserialization(); + testEOL.checkFailures(); + testEOL.dispose(); + } +} \ No newline at end of file diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TextField/TextFieldEditing/TextFieldEditing.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/awt/TextField/TextFieldEditing/TextFieldEditing.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8060137 + @library ../../regtesthelpers + @build Util + @summary Test TextField setText API + @run main TextFieldEditing + */ + +import java.awt.Frame; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.AWTException; +import java.awt.event.KeyEvent; +import test.java.awt.regtesthelpers.Util; + +public class TextFieldEditing { + + final static Robot robot = Util.createRobot(); + private int testFailCount; + private boolean isTestFail; + private StringBuilder testFailMessage; + + private Frame mainFrame; + private TextField textField; + + private TextFieldEditing() { + testFailMessage = new StringBuilder(); + mainFrame = new Frame(); + mainFrame.setSize(200, 200); + + textField = new TextField(); + mainFrame.add(textField); + mainFrame.setVisible(true); + } + + private void dispose() { + if (mainFrame != null) { + mainFrame.dispose(); + } + } + + public static void main(String[] s) { + TextFieldEditing textField = new TextFieldEditing(); + textField.testSetText(); + textField.checkFailures(); + textField.dispose(); + } + + private void testSetText() { + textField.setText(null); + textField.requestFocus(); + Util.clickOnComp(textField, robot); + Util.waitForIdle(robot); + robot.keyPress(KeyEvent.VK_A); + robot.delay(5); + robot.keyRelease(KeyEvent.VK_A); + Util.waitForIdle(robot); + textField.setText(null); + checkTest(""); + textField.setText("CaseSensitive"); + checkTest("CaseSensitive"); + textField.setText("caseSensitive"); + checkTest("caseSensitive"); + } + + private void checkTest(String str) { + if (str != null && !str.equals(textField.getText())) { + testFailMessage.append("TestFail line : "); + testFailMessage.append(Thread.currentThread().getStackTrace()[2]. + getLineNumber()); + testFailMessage.append(" TextField string : \""); + testFailMessage.append(textField.getText()); + testFailMessage.append("\" does not match expected string : \""); + testFailMessage.append(str).append("\""); + testFailMessage.append(System.getProperty("line.separator")); + testFailCount++; + isTestFail = true; + } + } + + private void checkFailures() { + if (isTestFail) { + testFailMessage.insert(0, "Test Fail count : " + testFailCount + + System.getProperty("line.separator")); + dispose(); + throw new RuntimeException(testFailMessage.toString()); + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/ActionCommand/ActionCommand.java --- a/jdk/test/java/awt/TrayIcon/ActionCommand/ActionCommand.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/ActionCommand/ActionCommand.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,8 +59,11 @@ "and rerun test."); } else if (System.getProperty("os.name").toLowerCase().startsWith("mac")){ isMacOS = true; + } else if (SystemTrayIconHelper.isOel7()) { + System.out.println("OEL 7 doesn't support double click in " + + "systray. Skipped"); + return; } - new ActionCommand().doTest(); } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/ActionEventMask/ActionEventMask.java --- a/jdk/test/java/awt/TrayIcon/ActionEventMask/ActionEventMask.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/ActionEventMask/ActionEventMask.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,6 +66,10 @@ } else { if (System.getProperty("os.name").toLowerCase().startsWith("mac")) { isMacOS = true; + } else if (SystemTrayIconHelper.isOel7()) { + System.out.println("OEL 7 doesn't support double click in " + + "systray. Skipped"); + return; } new ActionEventMask().doTest(); } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/ModalityTest/ModalityTest.java --- a/jdk/test/java/awt/TrayIcon/ModalityTest/ModalityTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/ModalityTest/ModalityTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ */ public class ModalityTest { + private static boolean isOEL7; TrayIcon icon; ExtendedRobot robot; Dialog d; @@ -80,7 +81,7 @@ "\"Always show all icons and notifications on the taskbar\" true " + "to avoid this problem. Or change behavior only for Java SE tray " + "icon and rerun test."); - + isOEL7 = SystemTrayIconHelper.isOel7(); new ModalityTest().doTest(); } } @@ -225,6 +226,12 @@ Point iconPosition = SystemTrayIconHelper.getTrayIconLocation(icon); if (iconPosition == null) throw new RuntimeException("Unable to find the icon location!"); + if (isOEL7) { + // close tray + robot.mouseMove(100,100); + robot.click(InputEvent.BUTTON1_MASK); + robot.waitForIdle(2000); + } if (! d.isVisible()) throw new RuntimeException("FAIL: The modal dialog is not yet visible"); @@ -232,27 +239,35 @@ robot.mouseMove(iconPosition.x, iconPosition.y); robot.waitForIdle(2000); - SystemTrayIconHelper.doubleClick(robot); + if(!isOEL7) { + SystemTrayIconHelper.doubleClick(robot); - if (! actionPerformed) { - synchronized (actionLock) { - try { - actionLock.wait(3000); - } catch (Exception e) { + if (!actionPerformed) { + synchronized (actionLock) { + try { + actionLock.wait(3000); + } catch (Exception e) { + } } } + if (!actionPerformed) + throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); } - if (! actionPerformed) - throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); for (int i = 0; i < buttonTypes.length; i++) { mousePressed = false; - robot.mousePress(buttonTypes[i]); + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mousePress(buttonTypes[i]); + } if (! mousePressed) { synchronized (pressLock) { try { - pressLock.wait(3000); + pressLock.wait(6000); } catch (Exception e) { } } @@ -264,12 +279,18 @@ mouseReleased = false; mouseClicked = false; - robot.mouseRelease(buttonTypes[i]); + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mouseRelease(buttonTypes[i]); + } if (! mouseReleased) { synchronized (releaseLock) { try { - releaseLock.wait(3000); + releaseLock.wait(6000); } catch (Exception e) { } } @@ -281,7 +302,7 @@ if (! mouseClicked) { synchronized (clickLock) { try { - clickLock.wait(3000); + clickLock.wait(6000); } catch (Exception e) { } } @@ -290,13 +311,14 @@ throw new RuntimeException("FAIL: mouseClicked not triggered when " + buttonNames[i] + " pressed & released"); } + if (!isOEL7) { + mouseMoved = false; + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.glide(iconPosition.x + 100, iconPosition.y); - mouseMoved = false; - robot.mouseMove(iconPosition.x, iconPosition.y); - robot.glide(iconPosition.x + 100, iconPosition.y); - - if (! mouseMoved) - if (! SystemTrayIconHelper.skip(0) ) - throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + if (!mouseMoved) + if (!SystemTrayIconHelper.skip(0)) + throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + } } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/MouseEventMask/MouseEventMaskTest.java --- a/jdk/test/java/awt/TrayIcon/MouseEventMask/MouseEventMaskTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/MouseEventMask/MouseEventMaskTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,6 +71,8 @@ "\"Always show all icons and notifications on the taskbar\" true " + "to avoid this problem. Or change behavior only for Java SE tray " + "icon and rerun test."); + } else if (SystemTrayIconHelper.isOel7()) { + return; } new MouseEventMaskTest().doTest(); } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/MouseMovedTest/MouseMovedTest.java --- a/jdk/test/java/awt/TrayIcon/MouseMovedTest/MouseMovedTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/MouseMovedTest/MouseMovedTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ * @summary Check for mouseMoved event for java.awt.TrayIcon * @author Dmitriy Ermashov (dmitriy.ermashov@oracle.com) * @library ../../../../lib/testlibrary - * @build ExtendedRobot + * @build ExtendedRobot SystemTrayIconHelper * @run main MouseMovedTest */ @@ -39,6 +39,14 @@ static volatile boolean moved; public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + return; + } + + if (SystemTrayIconHelper.isOel7()) { + return; + } + moved = false; TrayIcon icon = new TrayIcon(new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB), "Test icon"); diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/FunctionalityCheck.java --- a/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/FunctionalityCheck.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/FunctionalityCheck.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,6 +51,7 @@ boolean mouseReleased = false; boolean mouseClicked = false; boolean mouseMoved = false; + static boolean isOEL7; static final int[] buttonTypes = { InputEvent.BUTTON1_MASK, @@ -69,6 +70,7 @@ System.out.println("SystemTray not supported on the platform under test. " + "Marking the test passed"); } else { + isOEL7 = SystemTrayIconHelper.isOel7(); new FunctionalityCheck().doTest(); } } @@ -188,31 +190,44 @@ Point iconPosition = SystemTrayIconHelper.getTrayIconLocation(icon); if (iconPosition == null) throw new RuntimeException("Unable to find the icon location!"); + if (isOEL7) { + // close tray + robot.mouseMove(100,100); + robot.click(InputEvent.BUTTON1_MASK); + robot.waitForIdle(2000); + } robot.mouseMove(iconPosition.x, iconPosition.y); - robot.waitForIdle(2000); - - SystemTrayIconHelper.doubleClick(robot); + robot.waitForIdle(); + if(!isOEL7) { + SystemTrayIconHelper.doubleClick(robot); - if (! actionPerformed) { - synchronized (actionLock) { - try { - actionLock.wait(3000); - } catch (Exception e) { + if (!actionPerformed) { + synchronized (actionLock) { + try { + actionLock.wait(3000); + } catch (Exception e) { + } } } + if (!actionPerformed) + throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); } - if (! actionPerformed) - throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); for (int i = 0; i < buttonTypes.length; i++) { mousePressed = false; - robot.mousePress(buttonTypes[i]); + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mousePress(buttonTypes[i]); + } if (! mousePressed) { synchronized (pressLock) { try { - pressLock.wait(3000); + pressLock.wait(6000); } catch (Exception e) { } } @@ -224,12 +239,17 @@ mouseReleased = false; mouseClicked = false; - robot.mouseRelease(buttonTypes[i]); - + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mouseRelease(buttonTypes[i]); + } if (! mouseReleased) { synchronized (releaseLock) { try { - releaseLock.wait(3000); + releaseLock.wait(6000); } catch (Exception e) { } } @@ -242,7 +262,7 @@ if (! mouseClicked) { synchronized (clickLock) { try { - clickLock.wait(3000); + clickLock.wait(6000); } catch (Exception e) { } } @@ -251,13 +271,14 @@ throw new RuntimeException("FAIL: mouseClicked not triggered when " + buttonNames[i] + " pressed & released"); } + if(!isOEL7) { + mouseMoved = false; + robot.mouseMove(iconPosition.x + 100, iconPosition.y); + robot.glide(iconPosition.x, iconPosition.y); - mouseMoved = false; - robot.mouseMove(iconPosition.x + 100, iconPosition.y); - robot.glide(iconPosition.x, iconPosition.y); - - if (! mouseMoved) - if (! SystemTrayIconHelper.skip(0) ) - throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + if (!mouseMoved) + if (!SystemTrayIconHelper.skip(0)) + throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + } } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/tray.policy --- a/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/tray.policy Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/tray.policy Wed Jul 05 21:10:34 2017 +0200 @@ -5,6 +5,7 @@ permission java.util.PropertyPermission "resultsDir", "read"; permission java.util.PropertyPermission "user.home", "read"; permission java.util.PropertyPermission "os.name", "read"; + permission java.util.PropertyPermission "os.version", "read"; permission java.awt.AWTPermission "accessEventQueue"; permission java.lang.RuntimePermission "setIO"; permission java.lang.RuntimePermission "accessDeclaredMembers"; @@ -17,5 +18,6 @@ permission java.util.PropertyPermission "java.class.path", "read"; permission java.awt.AWTPermission "readDisplayPixels"; permission java.awt.AWTPermission "watchMousePointer"; + }; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/SystemTrayIconHelper.java --- a/jdk/test/java/awt/TrayIcon/SystemTrayIconHelper.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/SystemTrayIconHelper.java Wed Jul 05 21:10:34 2017 +0200 @@ -66,7 +66,9 @@ for (int x = (int) (screenSize.getWidth()-width); x > 0; x--) { for (int y = (int) (screenSize.getHeight()-height); y > (screenSize.getHeight()-50); y--) { if (imagesEquals(((BufferedImage)icon.getImage()).getSubimage(0, 0, width, height), screen.getSubimage(x, y, width, height))) { - return new Point(x+5, y+5); + Point point = new Point(x + 5, y + 5); + System.out.println("Icon location " + point); + return point; } } } @@ -91,6 +93,7 @@ point2d = (Point2D)m_getLocation.invoke(peer, new Object[]{model}); Point po = new Point((int)(point2d.getX()), (int)(point2d.getY())); po.translate(10, -5); + System.out.println("Icon location " + po); return po; }catch(Exception e) { e.printStackTrace(); @@ -101,12 +104,15 @@ // sun.awt.X11.XTrayIconPeer Field f_peer = getField(java.awt.TrayIcon.class, "peer"); + SystemTrayIconHelper.openTrayIfNeeded(robot); + Object peer = f_peer.get(icon); Method m_getLOS = peer.getClass().getDeclaredMethod( "getLocationOnScreen", new Class[]{}); m_getLOS.setAccessible(true); Point point = (Point)m_getLOS.invoke(peer, new Object[]{}); point.translate(5, 5); + System.out.println("Icon location " + point); return point; } catch (Exception e) { e.printStackTrace(); @@ -169,4 +175,38 @@ } return false; } + + public static boolean openTrayIfNeeded(Robot robot) { + String sysv = System.getProperty("os.version"); + System.out.println("System version is " + sysv); + //Additional step to raise the system try in Gnome 3 in OEL 7 + if(isOel7()) { + System.out.println("OEL 7 detected"); + GraphicsConfiguration gc = GraphicsEnvironment. + getLocalGraphicsEnvironment().getDefaultScreenDevice(). + getDefaultConfiguration(); + Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc); + if(insets.bottom > 0) { + Dimension screenSize = Toolkit.getDefaultToolkit() + .getScreenSize(); + robot.mouseMove(screenSize.width - insets.bottom / 2, + screenSize.height - insets.bottom / 2); + robot.delay(50); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.delay(50); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.waitForIdle(); + robot.delay(1000); + System.out.println("Tray is opened"); + return true; + } + } + return false; + } + + public static boolean isOel7() { + return System.getProperty("os.name").toLowerCase() + .contains("linux") && System.getProperty("os.version") + .toLowerCase().contains("el7"); + } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/TrayIconEventModifiers/TrayIconEventModifiersTest.java --- a/jdk/test/java/awt/TrayIcon/TrayIconEventModifiers/TrayIconEventModifiersTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/TrayIconEventModifiers/TrayIconEventModifiersTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -121,6 +121,12 @@ }; } + if (SystemTrayIconHelper.isOel7()) { + System.out.println("OEL 7 doesn't support click modifiers in " + + "systray. Skipped"); + return; + } + new TrayIconEventModifiersTest().doTest(); } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/TrayIconEvents/TrayIconEventsTest.java --- a/jdk/test/java/awt/TrayIcon/TrayIconEvents/TrayIconEventsTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/TrayIconEvents/TrayIconEventsTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ public class TrayIconEventsTest { + private static boolean isOEL7; TrayIcon icon; ExtendedRobot robot; @@ -77,6 +78,7 @@ "\"Always show all icons and notifications on the taskbar\" true " + "to avoid this problem. Or change behavior only for Java SE " + "tray icon."); + isOEL7 = SystemTrayIconHelper.isOel7(); new TrayIconEventsTest().doTest(); } } @@ -195,31 +197,44 @@ Point iconPosition = SystemTrayIconHelper.getTrayIconLocation(icon); if (iconPosition == null) throw new RuntimeException("Unable to find the icon location!"); + if (isOEL7) { + // close tray + robot.mouseMove(100,100); + robot.click(InputEvent.BUTTON1_MASK); + robot.waitForIdle(2000); + } robot.mouseMove(iconPosition.x, iconPosition.y); - robot.waitForIdle(2000); - - SystemTrayIconHelper.doubleClick(robot); + robot.waitForIdle(); + if(!isOEL7) { + SystemTrayIconHelper.doubleClick(robot); - if (! actionPerformed) { - synchronized (actionLock) { - try { - actionLock.wait(10000); - } catch (Exception e) { + if (!actionPerformed) { + synchronized (actionLock) { + try { + actionLock.wait(10000); + } catch (Exception e) { + } } } + if (!actionPerformed) + throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); } - if (! actionPerformed) - throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); for (int i = 0; i < buttonTypes.length; i++) { mousePressed = false; - robot.mousePress(buttonTypes[i]); + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mousePress(buttonTypes[i]); + } if (! mousePressed) { synchronized (pressLock) { try { - pressLock.wait(3000); + pressLock.wait(6000); } catch (Exception e) { } } @@ -231,12 +246,18 @@ mouseReleased = false; mouseClicked = false; - robot.mouseRelease(buttonTypes[i]); + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mouseRelease(buttonTypes[i]); + } if (! mouseReleased) { synchronized (releaseLock) { try { - releaseLock.wait(3000); + releaseLock.wait(6000); } catch (Exception e) { } } @@ -248,7 +269,7 @@ if (! mouseClicked) { synchronized (clickLock) { try { - clickLock.wait(3000); + clickLock.wait(6000); } catch (Exception e) { } } @@ -258,12 +279,14 @@ buttonNames[i] + " pressed & released"); } - mouseMoved = false; - robot.mouseMove(iconPosition.x + 100, iconPosition.y); - robot.glide(iconPosition.x, iconPosition.y); + if (!isOEL7) { + mouseMoved = false; + robot.mouseMove(iconPosition.x + 100, iconPosition.y); + robot.glide(iconPosition.x, iconPosition.y); - if (! mouseMoved) - if (! SystemTrayIconHelper.skip(0) ) - throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + if (!mouseMoved) + if (!SystemTrayIconHelper.skip(0)) + throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + } } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/TrayIconMouseTest/TrayIconMouseTest.java --- a/jdk/test/java/awt/TrayIcon/TrayIconMouseTest/TrayIconMouseTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/TrayIconMouseTest/TrayIconMouseTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,6 +66,10 @@ } else { if (System.getProperty("os.name").toLowerCase().startsWith("mac")) { isMacOS = true; + } else if (SystemTrayIconHelper.isOel7()) { + System.out.println("OEL 7 doesn't support double click in " + + "systray. Skipped"); + return; } new TrayIconMouseTest().doTest(); } @@ -108,7 +112,7 @@ for (int i = 0; i < buttonTypes.length; i++) { actionPerformed = false; robot.click(buttonTypes[i]); - robot.waitForIdle(2000); + robot.waitForIdle(6000); if (isMacOS && actionPerformed && i == 2) { @@ -155,7 +159,7 @@ if (! actionPerformed) { synchronized (actionLock) { try { - actionLock.wait(3000); + actionLock.wait(6000); } catch (Exception e) { } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/TrayIcon/TrayIconPopup/TrayIconPopupTest.java --- a/jdk/test/java/awt/TrayIcon/TrayIconPopup/TrayIconPopupTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/awt/TrayIcon/TrayIconPopup/TrayIconPopupTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -127,7 +127,7 @@ robot.mousePress(InputEvent.BUTTON3_MASK); robot.delay(50); robot.mouseRelease(InputEvent.BUTTON3_MASK); - robot.delay(1000); + robot.delay(6000); robot.mouseMove(window.getLocation().x + 10, window.getLocation().y + 10); robot.mousePress(InputEvent.BUTTON3_MASK); diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/font/GlyphVector/TestStandardGlyphVectorBug.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/awt/font/GlyphVector/TestStandardGlyphVectorBug.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; + +/** + * @test + * @bug 7160052 + * @run main TestStandardGlyphVectorBug + * @summary GlyphVector.setGlyphPosition should not throw an exception on valid input + */ +public class TestStandardGlyphVectorBug +{ + public static void main(String[] args) + { + Font defaultFont = new Font(null); + FontRenderContext defaultFrc = new FontRenderContext(new AffineTransform(), + true, true); + GlyphVector gv = defaultFont.createGlyphVector(defaultFrc, "test"); + + //this causes the bounds to be cached + //which is necessary to trigger the bug + gv.getGlyphLogicalBounds(0); + + //this correctly gets the position of the overall advance + Point2D glyphPosition = gv.getGlyphPosition(gv.getNumGlyphs()); + + // this sets the position of the overall advance, + // but also incorrectly tries to clear the bounds cache + // of a specific glyph indexed by the glyphIndex parameter + // even if the glyphIndex represents the overall advance + // (i.e. if glyphIndex == getNumGlyphs()) + gv.setGlyphPosition(gv.getNumGlyphs(), glyphPosition); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/awt/image/VolatileImage/VolatileImageBug.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/awt/image/VolatileImage/VolatileImageBug.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.image.VolatileImage; + +/** + * @test + * @bug 8140530 + * @run main VolatileImageBug + * @summary Creating volatileimage(0,0) should throw IAE + */ +public class VolatileImageBug { + public static void main(String[] args) { + + boolean iaeThrown = false; + GraphicsEnvironment ge = GraphicsEnvironment. + getLocalGraphicsEnvironment(); + GraphicsConfiguration gc = ge.getDefaultScreenDevice(). + getDefaultConfiguration(); + try { + VolatileImage volatileImage = gc.createCompatibleVolatileImage(0, 0); + } catch (IllegalArgumentException iae) { + iaeThrown = true; + } + if (!iaeThrown) { + throw new RuntimeException ("IllegalArgumentException not thrown " + + "for createCompatibleVolatileImage(0,0)"); + } + } +} + diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/beans/XMLEncoder/javax_swing_JComponent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/beans/XMLEncoder/javax_swing_JComponent.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; + +/* + * @test + * @bug 8131754 + * @summary Tests JComponent encoding + */ +public final class javax_swing_JComponent extends AbstractTest { + + public static void main(final String[] args) { + new javax_swing_JComponent().test(true); + } + + protected JComponent getObject() { + return new SimpleJComponent(); + } + + protected JComponent getAnotherObject() { + return new CustomJComponent(); + } + + public static final class SimpleJComponent extends JComponent { + + } + + public static final class CustomJComponent extends JComponent { + + public CustomJComponent() { + ui = new CustomUI(); + } + + @Override + public ComponentUI getUI() { + return ui; + } + + @Override + public void setUI(final ComponentUI newUI) { + ui = newUI; + } + } + + public static final class CustomUI extends ComponentUI { + + public boolean getFlag() { + throw new Error(); + } + + public void setFlag(final boolean flag) { + throw new Error(); + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/lang/ProcessHandle/InfoTest.java --- a/jdk/test/java/lang/ProcessHandle/InfoTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/lang/ProcessHandle/InfoTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -49,7 +49,7 @@ /* * @test - * @bug 8077350 8081566 8081567 8098852 + * @bug 8077350 8081566 8081567 8098852 8136597 * @build jdk.testlibrary.* * @library /lib/testlibrary * @summary Functions of ProcessHandle.Info @@ -210,10 +210,12 @@ Assert.assertTrue(commandLine.contains(allArgs.get(i)), "commandLine() must contain argument: " + allArgs.get(i)); } - } else if (info.commandLine().isPresent()) { + } else if (info.commandLine().isPresent() && + command.isPresent() && + command.get().length() < info.commandLine().get().length()) { // If we only have the commandLine() we can only do some basic checks... String commandLine = info.commandLine().get(); - String javaExe = "java" + (Platform.isWindows() ? ".exe": ""); + String javaExe = "java" + (Platform.isWindows() ? ".exe" : ""); int pos = commandLine.indexOf(javaExe); Assert.assertTrue(pos > 0, "commandLine() should at least contain 'java'"); diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/lang/ref/CleanerTest.java --- a/jdk/test/java/lang/ref/CleanerTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/lang/ref/CleanerTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -28,9 +28,9 @@ import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.Objects; -import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; @@ -38,15 +38,21 @@ import jdk.internal.misc.CleanerImpl.WeakCleanable; import jdk.internal.misc.CleanerImpl.SoftCleanable; +import sun.hotspot.WhiteBox; + import org.testng.Assert; import org.testng.TestNG; import org.testng.annotations.Test; /* * @test - * @library /lib/testlibrary + * @library /lib/testlibrary /test/lib + * @build sun.hotspot.WhiteBox * @modules java.base/jdk.internal.misc - * @run testng/othervm -Xmx4m CleanerTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run testng/othervm + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -verbose:gc -Xmx4m CleanerTest */ @Test @@ -54,6 +60,9 @@ // A common CleaningService used by the test for notifications static final Cleaner COMMON = Cleaner.create(); + // Access to WhiteBox utilities + static final WhiteBox whitebox = WhiteBox.getWhiteBox(); + /** * Test that sequences of the various actions on a Reference * and on the Cleanable instance have the desired result. @@ -265,50 +274,29 @@ } /** - * Check a set of semaphores having been released by cleanup handlers. + * Check a semaphore having been released by cleanup handler. * Force a number of GC cycles to give the GC a chance to process - * all the References and for the cleanup actions to be run. + * the Reference and for the cleanup action to be run. * - * @param semaphore a varargs list of Semaphores - * @return true if all of the semaphores have at least 1 permit, - * false otherwise. + * @param semaphore a Semaphore + * @return true if the semaphores has 1 permit, false otherwise. */ - static boolean checkCleaned(Semaphore... semaphore) { - long[] cycles = new long[semaphore.length]; - long total = 0; - for (int cycle = 0; cycle < 20; cycle++) { - for (int i = 0; i < semaphore.length; i++) { - long count = semaphore[i].availablePermits(); - if (count > 0 && cycles[i] == 0) { - System.out.printf(" Cleanable[%d] cleaned in cycle: %d%n", i, cycle); - cycles[i] = cycle; - total += 1; + static boolean checkCleaned(Semaphore semaphore) { + int cycle = 0; + for (; cycle < 3; cycle++) { + try { + if (semaphore.tryAcquire(10L, TimeUnit.MILLISECONDS)) { + System.out.printf(" Cleanable cleaned in cycle: %d%n", cycle); + return true; } - } - - if (total == semaphore.length) { - System.out.printf(" All cleanups done in cycle: %d, total: %d%n", - cycle, total); - for (int i = 0; i < semaphore.length; i++) { - long count = semaphore[i].availablePermits(); - Assert.assertEquals(count, 1, - "Cleanable invoked more than once, semaphore " + i); - } - return true; // all references freed + } catch (InterruptedException ie) { + // retry in outer loop } // Force GC - memoryPressure(); + whitebox.fullGC(); } - // Not all objects have been cleaned - - for (int i = 0; i < semaphore.length; i++) { - if (cycles[i] != 0) { - System.out.printf(" Cleanable[%d] cleaned in cycle: %d%n", i, cycles[i]); - } else { - System.out.printf(" Cleanable[%d] not cleaned%n", i); - } - } - + // Object has not been cleaned + System.out.printf(" Cleanable not cleaned%n"); return false; // Failing result } @@ -457,24 +445,6 @@ } /** - * MemoryPressure allocates memory to force a gc and to clear SoftReferences. - */ - static void memoryPressure() { - SoftReference soft = new SoftReference<>(new Object(), null); - Vector root = new Vector<>(); - try { - long free = 0; - while (soft.get() != null) { - long[] extra = new long[50_000]; - root.addElement(extra); - } - } catch (OutOfMemoryError mem) { - // ignore - root = null; - } - } - - /** * CleanableCase encapsulates the objects used for a test. * The reference to the object is not held directly, * but in a Reference object that can be cleared. diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/net/NetworkInterface/NetworkInterfaceStreamTest.java --- a/jdk/test/java/net/NetworkInterface/NetworkInterfaceStreamTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/net/NetworkInterface/NetworkInterfaceStreamTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -85,13 +85,15 @@ } private Stream allNetworkInterfaces() throws SocketException { - return NetworkInterface.networkInterfaces().flatMap(this::allSubNetworkInterfaces); + return NetworkInterface.networkInterfaces() + .filter(ni -> isIncluded(ni)) + .flatMap(this::allSubNetworkInterfaces); } private Stream allSubNetworkInterfaces(NetworkInterface ni) { return Stream.concat( Stream.of(ni), - ni.subInterfaces().flatMap(this::allSubNetworkInterfaces)); + ni.subInterfaces().filter(sni -> isIncluded(sni)).flatMap(this::allSubNetworkInterfaces)); } @Test @@ -129,7 +131,9 @@ Collection nis = Collections.list(NetworkInterface.getNetworkInterfaces()); Collection expected = new ArrayList<>(); for (NetworkInterface ni : nis) { - expected.addAll(Collections.list(ni.getInetAddresses())); + if (isIncluded(ni)) { + expected.addAll(Collections.list(ni.getInetAddresses())); + } } withData(TestData.Factory.ofSupplier("All inet addresses", ss)) .stream(s -> s) diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/net/URL/TestPort.java --- a/jdk/test/java/net/URL/TestPort.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/net/URL/TestPort.java Wed Jul 05 21:10:34 2017 +0200 @@ -23,28 +23,33 @@ /* * @test - * @bug 4101492 4444213 + * @bug 4101492 4444213 4906983 * @summary The java.net.URL constructor allows port < 0 and port > 65535 */ import java.net.*; public class TestPort { - public static void main(String[] args) { - URL url = null; + public static void main(String[] args) throws MalformedURLException { + // URLs are able to have port bigger than TCP-Port 65535 and + // to have no port (-1) at all.: + URL url = new URL("http","server",Integer.MAX_VALUE,"/path"); + url = new URL("http://server:"+Integer.MAX_VALUE+"/path"); + url = new URL("http://server/path"); + url = new URL("http","server",-1,"/path"); + try { url = new URL("ftp", "java.sun.com", -20, "/pub/"); + throw new RuntimeException("MalformedURLException not thrown!"); } catch (MalformedURLException e) { - url = null; + // Everything fine. MalformedURLException expected } - if (url != null) - throw new RuntimeException("MalformedURLException not thrown!"); + try { url = new URL("ftp://java.sun.com:-20/pub/"); + throw new RuntimeException("MalformedURLException not thrown!"); } catch (MalformedURLException e) { - url = null; + // Everything fine. MalformedURLException expected } - if (url != null) - throw new RuntimeException("MalformedURLException not thrown!"); } } diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/nio/file/WatchService/UpdateInterference.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/nio/file/WatchService/UpdateInterference.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8145981 + * @summary LinuxWatchService sometimes reports inotify events against wrong directory + * @run main UpdateInterference + */ +import java.io.IOException; +import java.nio.file.*; +import java.util.List; +import java.util.concurrent.TimeUnit; +import static java.nio.file.StandardWatchEventKinds.*; + +public class UpdateInterference { + public static void main(String[] args) throws IOException, InterruptedException { + final Path root = Files.createTempDirectory("test"); + final Path foo = root.resolve("foo"); + final Path bar = root.resolve("bar"); + final Path baz = root.resolve("baz"); + + Files.createDirectory(foo); + Files.createDirectory(bar); + Files.createDirectory(baz); + + final WatchService watcher = root.getFileSystem().newWatchService(); + final WatchKey fooKey = foo.register(watcher, ENTRY_CREATE); + final WatchKey barKey = bar.register(watcher, ENTRY_CREATE); + + new Thread() { + { setDaemon(true); } + + @Override + public void run() { + while (true) { + try { + final Path temp = Files.createTempFile(foo, "temp", ".tmp"); + Files.delete(temp); + Thread.sleep(10); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + } + }.start(); + + new Thread() { + { setDaemon(true); } + + @Override + public void run() { + WatchKey bazKeys[] = new WatchKey[32]; + while (true) { + try { + for( int i = 0; i < bazKeys.length; i++) { + bazKeys[i] = baz.register(watcher, ENTRY_CREATE); + } + for( int i = 0; i < bazKeys.length; i++) { + bazKeys[i].cancel(); + } + Thread.sleep(1); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + } + }.start(); + + long time = System.currentTimeMillis(); + while ((System.currentTimeMillis() - time) < 15000) { + final WatchKey key = watcher.poll(60, TimeUnit.SECONDS); + if (key == null) continue; + + if (key != fooKey) { + List> pollEvents = key.pollEvents(); + for (WatchEvent watchEvent : pollEvents) { + System.out.println(watchEvent.count() + " " + + watchEvent.kind() + " " + + watchEvent.context()); + } + throw new RuntimeException("Event received for unexpected key"); + } + key.reset(); + } + } +} + diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/time/tck/java/time/TCKLocalDate.java --- a/jdk/test/java/time/tck/java/time/TCKLocalDate.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/time/tck/java/time/TCKLocalDate.java Wed Jul 05 21:10:34 2017 +0200 @@ -2157,6 +2157,31 @@ } //----------------------------------------------------------------------- + // toEpochSecond + //----------------------------------------------------------------------- + @DataProvider(name="epochSecond") + Object[][] provider_toEpochSecond() { + return new Object[][] { + {LocalDate.of(1858, 11, 17).toEpochSecond(LocalTime.MIDNIGHT, OFFSET_PONE), -3506720400L}, + {LocalDate.of(1, 1, 1).toEpochSecond(LocalTime.NOON, OFFSET_PONE), -62135557200L}, + {LocalDate.of(1995, 9, 27).toEpochSecond(LocalTime.of(5, 30), OFFSET_PTWO), 812172600L}, + {LocalDate.of(1970, 1, 1).toEpochSecond(LocalTime.MIDNIGHT, OFFSET_MTWO), 7200L}, + {LocalDate.of(-1, 12, 31).toEpochSecond(LocalTime.NOON, OFFSET_PONE), -62167266000L}, + {LocalDate.of(1, 1, 1).toEpochSecond(LocalTime.MIDNIGHT, OFFSET_PONE), + Instant.ofEpochSecond(-62135600400L).getEpochSecond()}, + {LocalDate.of(1995, 9, 27).toEpochSecond(LocalTime.NOON, OFFSET_PTWO), + Instant.ofEpochSecond(812196000L).getEpochSecond()}, + {LocalDate.of(1995, 9, 27).toEpochSecond(LocalTime.of(5, 30), OFFSET_MTWO), + LocalDateTime.of(1995, 9, 27, 5, 30).toEpochSecond(OFFSET_MTWO)}, + }; + } + + @Test(dataProvider="epochSecond") + public void test_toEpochSecond(long actual, long expected) { + assertEquals(actual, expected); + } + + //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- @Test diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/time/tck/java/time/TCKLocalTime.java --- a/jdk/test/java/time/tck/java/time/TCKLocalTime.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/time/tck/java/time/TCKLocalTime.java Wed Jul 05 21:10:34 2017 +0200 @@ -2433,6 +2433,32 @@ } } + //----------------------------------------------------------------------- + // toEpochSecond() + //-------------------------------------------------------------------------- + @DataProvider(name="epochSecond") + Object[][] provider__toEpochSecond() { + return new Object[][] { + {LocalTime.of(0, 0).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO), -7200L}, + {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1965, 12, 31), OFFSET_PTWO), -126282600L}, + {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1995, 5, 3), OFFSET_MTWO), 799507800L}, + {LocalTime.of(0, 0).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO), + Instant.ofEpochSecond(-7200).getEpochSecond()}, + {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1969, 12, 31), OFFSET_MTWO), + Instant.ofEpochSecond(-37800L).getEpochSecond()}, + {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO), + LocalDateTime.of(1970, 1, 1, 11, 30).toEpochSecond(OFFSET_PTWO)}, + }; + } + + @Test(dataProvider="epochSecond") + public void test_toEpochSecond(long actual, long expected) { + assertEquals(actual, expected); + } + + //----------------------------------------------------------------------- + // toSecondOfDay_fromNanoOfDay_symmetry() + //----------------------------------------------------------------------- @Test public void test_toSecondOfDay_fromNanoOfDay_symmetry() { LocalTime t = LocalTime.of(0, 0); diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/time/tck/java/time/TCKOffsetTime.java --- a/jdk/test/java/time/tck/java/time/TCKOffsetTime.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/time/tck/java/time/TCKOffsetTime.java Wed Jul 05 21:10:34 2017 +0200 @@ -134,6 +134,7 @@ private static final ZoneId ZONE_GAZA = ZoneId.of("Asia/Gaza"); private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2); private static final LocalDate DATE = LocalDate.of(2008, 12, 3); private OffsetTime TEST_11_30_59_500_PONE; @@ -1149,6 +1150,29 @@ } //----------------------------------------------------------------------- + // toEpochSecond() + //----------------------------------------------------------------------- + @DataProvider(name="epochSecond") + Object[][] provider_toEpochSecond() { + return new Object[][] { + {OffsetTime.of(0, 0, 0, 0, OFFSET_PTWO).toEpochSecond(LocalDate.of(1970, 1, 1)), -7200L}, + {OffsetTime.of(11, 30, 0, 0, OFFSET_MTWO).toEpochSecond(LocalDate.of(1995, 9, 27)), 812208600L}, + {OffsetTime.of(0, 0, 0, 0, OFFSET_PONE).toEpochSecond(LocalDate.of(1970, 1, 1)), + Instant.ofEpochSecond(-3600).getEpochSecond()}, + {OffsetTime.of(11, 30, 0, 0, OFFSET_PTWO).toEpochSecond(LocalDate.of(1965, 12, 31)), + Instant.ofEpochSecond(-126282600L).getEpochSecond()}, + {OffsetTime.of(11, 30, 0, 0, OFFSET_MTWO).toEpochSecond(LocalDate.of(1970, 1, 1)), + OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of(11, 30), OFFSET_MTWO) + .toEpochSecond()}, + }; + } + + @Test(dataProvider="epochSecond") + public void test_toEpochSecond(long actual, long expected) { + assertEquals(actual, expected); + } + + //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- @Test diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/time/tck/java/time/TCKZonedDateTime.java --- a/jdk/test/java/time/tck/java/time/TCKZonedDateTime.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/time/tck/java/time/TCKZonedDateTime.java Wed Jul 05 21:10:34 2017 +0200 @@ -751,7 +751,7 @@ {"2012-06-30T12:30:40Z[GMT]", 2012, 6, 30, 12, 30, 40, 0, "GMT"}, {"2012-06-30T12:30:40Z[UT]", 2012, 6, 30, 12, 30, 40, 0, "UT"}, {"2012-06-30T12:30:40Z[UTC]", 2012, 6, 30, 12, 30, 40, 0, "UTC"}, - {"2012-06-30T12:30:40+01:00[Z]", 2012, 6, 30, 12, 30, 40, 0, "Z"}, + {"2012-06-30T12:30:40+01:00[Z]", 2012, 6, 30, 11, 30, 40, 0, "Z"}, {"2012-06-30T12:30:40+01:00[+01:00]", 2012, 6, 30, 12, 30, 40, 0, "+01:00"}, {"2012-06-30T12:30:40+01:00[GMT+01:00]", 2012, 6, 30, 12, 30, 40, 0, "GMT+01:00"}, {"2012-06-30T12:30:40+01:00[UT+01:00]", 2012, 6, 30, 12, 30, 40, 0, "UT+01:00"}, diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/time/tck/java/time/format/TCKDTFParsedInstant.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/time/tck/java/time/format/TCKDTFParsedInstant.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 tck.java.time.format; + +import static org.testng.AssertJUnit.assertEquals; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Testing DateTimeFormatter Parsing with 4 different test conditions: + * 1. When Zone and Offset not provided + * 2. When Zone and Offset provided + * 3. When Offset is not provided and Zone is provided + * 4. When Zone is not provided and Offset is provided + */ + +@Test +public class TCKDTFParsedInstant { + + private static final ZoneId EUROPE_BERLIN = ZoneId.of("Europe/Berlin"); + private static final ZoneId ASIA_ISTANBUL = ZoneId.of("Asia/Istanbul"); + + private DateTimeFormatter dtFormatter; + private ZonedDateTime zdt1, zdt2; + private LocalDateTime ldt1; + private OffsetDateTime odt1; + + @BeforeMethod + public void setUp() throws Exception { + dtFormatter = DateTimeFormatter.ISO_ZONED_DATE_TIME; + } + + @DataProvider(name="parseWithoutZoneWithoutOffset") + Object[][] data_parse_WithoutOffset_WithoutZone() { + return new Object[][] { + {"1966-12-31T00:01:10", LocalDateTime.of(1966, 12, 31, 0, 1, 10)}, + {"1970-01-01T00:00:00", LocalDateTime.of(1970, 1, 1, 0, 0, 0)}, + {"2004-02-29T00:30:00", LocalDateTime.of(2004, 2, 29, 0, 30, 0)}, + {"2015-12-31T23:59:59", LocalDateTime.of(2015, 12, 31, 23, 59, 59)} + }; + } + + @Test(dataProvider="parseWithoutZoneWithoutOffset") + public void testWithoutZoneWithoutOffset(String ldtString, LocalDateTime expectedLDT) { + dtFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + ldt1 = LocalDateTime.parse(ldtString, dtFormatter); + assertEquals(expectedLDT, ldt1); + } + + @DataProvider(name="parseWithZoneWithOffset") + Object[][] data_parse_WithZone_WithOffset() { + return new Object[][] { + {"2012-10-28T01:45:00-02:30[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("-02:30"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00-01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("-01:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00-00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("-00:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00+00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("+00:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00+01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("+01:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00+02:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("+02:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00+03:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("+03:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00-02:30[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-02:30"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00-01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-01:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00-00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-00:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00+00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+00:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00+01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+01:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00+02:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+02:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00+03:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+03:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00-02:30[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-02:30"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00-01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-01:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00-00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-00:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00+00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+00:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00+01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+01:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00+02:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+02:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00+03:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+03:00"), EUROPE_BERLIN}, + + {"2012-10-28T02:45:00-02:30[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-02:30"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00-01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-01:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00-00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-00:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00+00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+00:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00+01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+01:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00+02:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+02:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00+03:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+03:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00-02:30[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-02:30"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00-01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-01:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00-00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-00:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00+00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+00:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00+01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+01:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00+02:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+02:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00+03:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+03:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00-02:30[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("-02:30"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00-01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("-01:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00-00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("-00:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00+00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("+00:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00+01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("+01:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00+02:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("+02:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00+03:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("+03:00"), ASIA_ISTANBUL} + }; + } + + @Test(dataProvider="parseWithZoneWithOffset") + public void testWithZoneWithOffset(String zdtString, LocalDateTime ldt, ZoneOffset offset, ZoneId zone) { + dtFormatter = DateTimeFormatter.ISO_ZONED_DATE_TIME; + zdt1 = ZonedDateTime.ofInstant(ldt, offset, zone); + zdt2 = ZonedDateTime.parse(zdtString, dtFormatter); + assertEquals(zdt1, zdt2); + } + + @DataProvider(name="parseWithZoneWithoutOffset") + Object[][] data_parse_WithZone_WithoutOffset() { + return new Object[][] { + {"28 Oct 00:45:00 2012 Europe/Berlin", ZonedDateTime.of(2012, 10, 28, 0, 45, 0, 0, EUROPE_BERLIN)}, + {"28 Oct 01:45:00 2012 Europe/Berlin", ZonedDateTime.of(2012, 10, 28, 1, 45, 0, 0, EUROPE_BERLIN)}, + {"28 Oct 02:45:00 2012 Europe/Berlin", ZonedDateTime.of(2012, 10, 28, 2, 45, 0, 0, EUROPE_BERLIN)}, + {"28 Oct 03:45:00 2012 Europe/Berlin", ZonedDateTime.of(2012, 10, 28, 3, 45, 0, 0, EUROPE_BERLIN)}, + {"28 Oct 04:45:00 2012 Europe/Berlin", ZonedDateTime.of(2012, 10, 28, 4, 45, 0, 0, EUROPE_BERLIN)}, + + {"28 Oct 01:45:00 2012 Asia/Istanbul", ZonedDateTime.of(2012, 10, 28, 1, 45, 0, 0, ASIA_ISTANBUL)}, + {"28 Oct 02:45:00 2012 Asia/Istanbul", ZonedDateTime.of(2012, 10, 28, 2, 45, 0, 0, ASIA_ISTANBUL)}, + {"28 Oct 03:45:00 2012 Asia/Istanbul", ZonedDateTime.of(2012, 10, 28, 3, 45, 0, 0, ASIA_ISTANBUL)}, + {"28 Oct 04:45:00 2012 Asia/Istanbul", ZonedDateTime.of(2012, 10, 28, 4, 45, 0, 0, ASIA_ISTANBUL)}, + {"28 Oct 05:45:00 2012 Asia/Istanbul", ZonedDateTime.of(2012, 10, 28, 5, 45, 0, 0, ASIA_ISTANBUL)} + }; + } + + @Test(dataProvider="parseWithZoneWithoutOffset") + public void testWithZoneWithoutOffset(String withZoneWithoutOffset, ZonedDateTime expectedZDT) { + dtFormatter = DateTimeFormatter.ofPattern("d MMM HH:mm:ss uuuu VV"); + zdt1 = ZonedDateTime.parse(withZoneWithoutOffset, dtFormatter); + assertEquals(expectedZDT, zdt1); + } + + @DataProvider(name="parseWithOffsetWithoutZone") + Object[][] data_parse_WithOffset_WithoutZone() { + return new Object[][] { + {"2015-12-14T00:45:00-11:30", OffsetDateTime.of(2015, 12, 14, 0, 45, 0, 0, ZoneOffset.of("-11:30"))}, + {"2015-12-14T01:45:00-05:00", OffsetDateTime.of(2015, 12, 14, 1, 45, 0, 0, ZoneOffset.of("-05:00"))}, + {"2015-12-14T02:45:00-00:00", OffsetDateTime.of(2015, 12, 14, 2, 45, 0, 0, ZoneOffset.of("-00:00"))}, + {"2015-12-14T03:45:00+00:00", OffsetDateTime.of(2015, 12, 14, 3, 45, 0, 0, ZoneOffset.of("+00:00"))}, + {"2015-12-14T04:45:00+03:30", OffsetDateTime.of(2015, 12, 14, 4, 45, 0, 0, ZoneOffset.of("+03:30"))}, + {"2015-12-14T05:45:00+10:00", OffsetDateTime.of(2015, 12, 14, 5, 45, 0, 0, ZoneOffset.of("+10:00"))} + }; + } + + @Test(dataProvider="parseWithOffsetWithoutZone") + public void testWithOffsetWithoutZone(String odtString, OffsetDateTime expectedOTD) { + dtFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + odt1 = OffsetDateTime.parse(odtString, dtFormatter); + assertEquals(expectedOTD, odt1); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/util/Formatter/Basic-X.java.template --- a/jdk/test/java/util/Formatter/Basic-X.java.template Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/util/Formatter/Basic-X.java.template Wed Jul 05 21:10:34 2017 +0200 @@ -36,7 +36,7 @@ import java.text.DateFormatSymbols; import java.util.*; #if[double] -import sun.misc.DoubleConsts; +import jdk.internal.math.DoubleConsts; #end[double] import static java.util.Calendar.*; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/util/Formatter/Basic.java --- a/jdk/test/java/util/Formatter/Basic.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/util/Formatter/Basic.java Wed Jul 05 21:10:34 2017 +0200 @@ -28,7 +28,7 @@ * 6344623 6369500 6534606 6282094 6286592 6476425 5063507 6469160 6476168 * 8059175 * - * @modules java.base/sun.misc + * @modules java.base/jdk.internal.math * @run shell/timeout=240 Basic.sh */ diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/java/util/Formatter/BasicDouble.java --- a/jdk/test/java/util/Formatter/BasicDouble.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/java/util/Formatter/BasicDouble.java Wed Jul 05 21:10:34 2017 +0200 @@ -36,7 +36,7 @@ import java.text.DateFormatSymbols; import java.util.*; -import sun.misc.DoubleConsts; +import jdk.internal.math.DoubleConsts; import static java.util.Calendar.*; @@ -1174,6 +1174,10 @@ + + + + //--------------------------------------------------------------------- // %f - float, double, Double, BigDecimal //--------------------------------------------------------------------- diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/imageio/plugins/jpeg/JpegImageColorSpaceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/imageio/plugins/jpeg/JpegImageColorSpaceTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8041501 + * @summary Test verifies if there is no JFIF & EXIF header + * and sampling factor is same of JPEG image, then + * imageIO should not override colorspace determined + * in IJG library. + * @run main JpegImageColorSpaceTest + */ + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.io.File; +import javax.imageio.ImageIO; + +public class JpegImageColorSpaceTest { + + public static void main(String args[]) throws Exception { + + String fileName = "nomarkers.jpg"; + String sep = System.getProperty("file.separator"); + String dir = System.getProperty("test.src", "."); + String filePath = dir+sep+fileName; + System.out.println("Test file: " + filePath); + File imageFile = new File(filePath); + + BufferedImage bufferedImage = ImageIO.read(imageFile); + int imageWidth = bufferedImage.getWidth(); + int imageHeight = bufferedImage.getHeight(); + + for (int i = 0; i < imageWidth; i++) { + for(int j = 0; j < imageHeight; j++) { + /* + * Since image is white we check individual pixel values from + * BufferedImage to verify if ImageIO.read() is done with proper + * color space or not. + */ + if (bufferedImage.getRGB(i, j) != Color.white.getRGB()) { + // color space is not proper + throw new RuntimeException("ColorSpace is not determined " + + "properly by ImageIO"); + } + } + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/imageio/plugins/jpeg/JpegMetadataColorSpaceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/imageio/plugins/jpeg/JpegMetadataColorSpaceTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8074967 + * @summary Test verifies if there is no JFIF & EXIF header + * and sampling factor is same of JPEG image, then + * JPEG colorspace should not be RGB. + * @run main JpegMetadataColorSpaceTest + */ + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataFormatImpl; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.ImageInputStream; +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +public class JpegMetadataColorSpaceTest { + public static void main(String[] args) throws IOException { + String fileName = "nomarkers.jpg"; + String sep = System.getProperty("file.separator"); + String dir = System.getProperty("test.src", "."); + String filePath = dir+sep+fileName; + System.out.println("Test file: " + filePath); + File file = new File(filePath); + ImageInputStream stream = ImageIO.createImageInputStream(file); + Iterator readers = ImageIO.getImageReaders(stream); + + if(readers.hasNext()) { + ImageReader reader = readers.next(); + reader.setInput(stream); + IIOMetadata metadata = reader.getImageMetadata(0); + + IIOMetadataNode standardTree = (IIOMetadataNode) + metadata.getAsTree + (IIOMetadataFormatImpl.standardMetadataFormatName); + IIOMetadataNode colorSpaceType = (IIOMetadataNode) + standardTree.getElementsByTagName("ColorSpaceType").item(0); + String colorSpaceName = colorSpaceType.getAttribute("name"); + if(colorSpaceName.equals("RGB")) + throw new RuntimeException("Identified incorrect ColorSpace"); + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/imageio/plugins/jpeg/nomarkers.jpg Binary file jdk/test/javax/imageio/plugins/jpeg/nomarkers.jpg has changed diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/imageio/plugins/png/PngForceStopWritingTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/imageio/plugins/png/PngForceStopWritingTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 6967419 + * @summary Test verifies that when we force stop PNG writing to + * ImageOutputStream, it should not cause IndexOutOfBoundException. + * @run main PngForceStopWritingTest + */ + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; +import javax.imageio.ImageIO; +import javax.imageio.stream.ImageOutputStream; + +public class PngForceStopWritingTest { + + public static void main(String[] args) throws IOException { + + OutputStream outputStream = new NullOutputStream(); + ImageOutputStream imageOutputStream = + ImageIO.createImageOutputStream(outputStream); + try { + ImageIO.write(createImage(2048),"PNG", imageOutputStream); + } catch (IOException e) { + imageOutputStream.close(); + } + } + + private static BufferedImage createImage(int size) { + + BufferedImage image = new + BufferedImage(size, size, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = image.createGraphics(); + g.setPaint(new GradientPaint(0, 0, Color.blue, size, size, Color.red)); + g.fillRect(0, 0, size, size); + g.dispose(); + return image; + } + + static class NullOutputStream extends OutputStream { + long count = 0; + @Override + public void write(int b) throws IOException { + count++; + if (count > 30000L) { + throw new IOException("Force stop image writing"); + } + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/imageio/plugins/shared/WriteAfterAbort.java --- a/jdk/test/javax/imageio/plugins/shared/WriteAfterAbort.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/javax/imageio/plugins/shared/WriteAfterAbort.java Wed Jul 05 21:10:34 2017 +0200 @@ -130,13 +130,25 @@ ImageWriterSpi.class, provider -> true, true); // Validates all supported ImageWriters + int numFailures = 0; while (iter.hasNext()) { final WriteAfterAbort writeAfterAbort = new WriteAfterAbort(); final ImageWriter writer = iter.next().createWriterInstance(); System.out.println("ImageWriter = " + writer); - writeAfterAbort.test(writer); + try { + writeAfterAbort.test(writer); + } catch (Exception e) { + System.err.println("Test failed for \"" + + writer.getOriginatingProvider().getFormatNames()[0] + + "\" format."); + numFailures++; + } } - System.out.println("Test passed"); + if (numFailures == 0) { + System.out.println("Test passed."); + } else { + throw new RuntimeException("Test failed."); + } } // Callbacks diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/imageio/plugins/tiff/WriteToSequenceAfterAbort.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/imageio/plugins/tiff/WriteToSequenceAfterAbort.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.imageio.ImageWriter; +import javax.imageio.event.IIOWriteProgressListener; +import javax.imageio.stream.ImageOutputStream; + +import static java.awt.image.BufferedImage.TYPE_BYTE_BINARY; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.util.Vector; +import javax.imageio.IIOImage; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; + +/** + * @test + * @bug 8144245 + * @summary Ensure aborting write works properly for a TIFF sequence. + */ +public final class WriteToSequenceAfterAbort implements IIOWriteProgressListener { + + private volatile boolean abortFlag = true; + private volatile boolean isAbortCalled; + private volatile boolean isCompleteCalled; + private volatile boolean isProgressCalled; + private volatile boolean isStartedCalled; + private static final int WIDTH = 100; + private static final int HEIGHT = 100; + private static final int NUM_TILES_XY = 3; + + private class TiledImage implements RenderedImage { + private final BufferedImage tile; + private final BufferedImage image; + private final int numXTiles, numYTiles; + private boolean isImageInitialized = false; + + TiledImage(BufferedImage tile, int numXTiles, int numYTiles) { + this.tile = tile; + this.numXTiles = numXTiles; + this.numYTiles = numYTiles; + image = new BufferedImage(getWidth(), getHeight(), tile.getType()); + } + + @Override + public Vector getSources() { + return null; + } + + @Override + public Object getProperty(String string) { + return java.awt.Image.UndefinedProperty; + } + + @Override + public String[] getPropertyNames() { + return new String[0]; + } + + @Override + public ColorModel getColorModel() { + return tile.getColorModel(); + } + + @Override + public SampleModel getSampleModel() { + return tile.getSampleModel(); + } + + @Override + public int getWidth() { + return numXTiles*tile.getWidth(); + } + + @Override + public int getHeight() { + return numYTiles*tile.getHeight(); + } + + @Override + public int getMinX() { + return 0; + } + + @Override + public int getMinY() { + return 0; + } + + @Override + public int getNumXTiles() { + return numXTiles; + } + + @Override + public int getNumYTiles() { + return numYTiles; + } + + @Override + public int getMinTileX() { + return 0; + } + + @Override + public int getMinTileY() { + return 0; + } + + @Override + public int getTileWidth() { + return tile.getWidth(); + } + + @Override + public int getTileHeight() { + return tile.getHeight(); + } + + @Override + public int getTileGridXOffset() { + return 0; + } + + @Override + public int getTileGridYOffset() { + return 0; + } + + @Override + public Raster getTile(int x, int y) { + WritableRaster r = tile.getRaster(); + return r.createWritableTranslatedChild(x*tile.getWidth(), + y*tile.getHeight()); + } + + @Override + public Raster getData() { + return getAsBufferedImage().getData(); + } + + @Override + public Raster getData(Rectangle r) { + return getAsBufferedImage().getData(r); + } + + @Override + public WritableRaster copyData(WritableRaster wr) { + return getAsBufferedImage().copyData(wr); + } + + public BufferedImage getAsBufferedImage() { + synchronized (image) { + if (!isImageInitialized) { + int tx0 = getMinTileX(), ty0 = getMinTileY(); + int txN = tx0 + getNumXTiles(), tyN = ty0 + getNumYTiles(); + for (int j = ty0; j < tyN; j++) { + for (int i = tx0; i < txN; i++) { + image.setData(getTile(i, j)); + } + } + } + isImageInitialized = true; + } + return image; + } + } + + private void test(final ImageWriter writer) throws IOException { + String suffix = writer.getOriginatingProvider().getFileSuffixes()[0]; + + // Image initialization + BufferedImage imageUpperLeft = + new BufferedImage(WIDTH, HEIGHT, TYPE_BYTE_BINARY); + Graphics2D g = imageUpperLeft.createGraphics(); + g.setColor(Color.WHITE); + g.fillRect(0, 0, WIDTH/2, HEIGHT/2); + g.dispose(); + BufferedImage imageLowerRight = + new BufferedImage(WIDTH, HEIGHT, TYPE_BYTE_BINARY); + g = imageLowerRight.createGraphics(); + g.setColor(Color.WHITE); + g.fillRect(WIDTH/2, HEIGHT/2, WIDTH/2, HEIGHT/2); + g.dispose(); + TiledImage[] images = new TiledImage[] { + new TiledImage(imageUpperLeft, NUM_TILES_XY, NUM_TILES_XY), + new TiledImage(imageUpperLeft, NUM_TILES_XY, NUM_TILES_XY), + new TiledImage(imageLowerRight, NUM_TILES_XY, NUM_TILES_XY), + new TiledImage(imageLowerRight, NUM_TILES_XY, NUM_TILES_XY) + }; + + // File initialization + File file = File.createTempFile("temp", "." + suffix); + file.deleteOnExit(); + FileOutputStream fos = new SkipWriteOnAbortOutputStream(file); + ImageOutputStream ios = ImageIO.createImageOutputStream(fos); + writer.setOutput(ios); + writer.addIIOWriteProgressListener(this); + + writer.prepareWriteSequence(null); + boolean[] abortions = new boolean[] {true, false, true, false}; + for (int i = 0; i < 4; i++) { + abortFlag = abortions[i]; + isAbortCalled = false; + isCompleteCalled = false; + isProgressCalled = false; + isStartedCalled = false; + + TiledImage image = images[i]; + if (abortFlag) { + // This write will be aborted, and file will not be touched + writer.writeToSequence(new IIOImage(image, null, null), null); + if (!isStartedCalled) { + throw new RuntimeException("Started should be called"); + } + if (!isProgressCalled) { + throw new RuntimeException("Progress should be called"); + } + if (!isAbortCalled) { + throw new RuntimeException("Abort should be called"); + } + if (isCompleteCalled) { + throw new RuntimeException("Complete should not be called"); + } + } else { + // This write should be completed successfully and the file should + // contain correct image data. + writer.writeToSequence(new IIOImage(image, null, null), null); + if (!isStartedCalled) { + throw new RuntimeException("Started should be called"); + } + if (!isProgressCalled) { + throw new RuntimeException("Progress should be called"); + } + if (isAbortCalled) { + throw new RuntimeException("Abort should not be called"); + } + if (!isCompleteCalled) { + throw new RuntimeException("Complete should be called"); + } + } + } + + writer.endWriteSequence(); + writer.dispose(); + ios.close(); + + // Validates content of the file. + ImageReader reader = ImageIO.getImageReader(writer); + ImageInputStream iis = ImageIO.createImageInputStream(file); + reader.setInput(iis); + for (int i = 0; i < 2; i++) { + System.out.println("Testing image " + i); + BufferedImage imageRead = reader.read(i); + BufferedImage imageWrite = images[2 * i].getAsBufferedImage(); + for (int x = 0; x < WIDTH; ++x) { + for (int y = 0; y < HEIGHT; ++y) { + if (imageRead.getRGB(x, y) != imageWrite.getRGB(x, y)) { + throw new RuntimeException("Test failed for image " + i); + } + } + } + } + } + + public static void main(final String[] args) throws IOException { + WriteToSequenceAfterAbort writeAfterAbort = new WriteToSequenceAfterAbort(); + ImageWriter writer = ImageIO.getImageWritersByFormatName("TIFF").next(); + writeAfterAbort.test(writer); + System.out.println("Test passed."); + } + + // Callbacks + + @Override + public void imageComplete(ImageWriter source) { + isCompleteCalled = true; + } + + @Override + public void imageProgress(ImageWriter source, float percentageDone) { + isProgressCalled = true; + if (percentageDone > 50 && abortFlag) { + source.abort(); + } + } + + @Override + public void imageStarted(ImageWriter source, int imageIndex) { + isStartedCalled = true; + } + + @Override + public void writeAborted(final ImageWriter source) { + isAbortCalled = true; + } + + @Override + public void thumbnailComplete(ImageWriter source) { + } + + @Override + public void thumbnailProgress(ImageWriter source, float percentageDone) { + } + + @Override + public void thumbnailStarted(ImageWriter source, int imageIndex, + int thumbnailIndex) { + } + + /** + * We need to skip writes on abort, because content of the file after abort + * is undefined. + */ + private class SkipWriteOnAbortOutputStream extends FileOutputStream { + + SkipWriteOnAbortOutputStream(File file) throws FileNotFoundException { + super(file); + } + + @Override + public void write(int b) throws IOException { + if (!abortFlag) { + super.write(b); + } + } + + @Override + public void write(byte[] b) throws IOException { + if (!abortFlag) { + super.write(b); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (!abortFlag) { + super.write(b, off, len); + } + } + } +} + diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/imageio/spi/MarkTryFinallyReproducer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/imageio/spi/MarkTryFinallyReproducer.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,367 @@ +/* + * Copyright 2015 Red Hat, Inc. + * 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 8144071 + * @run main/othervm MarkTryFinallyReproducer + * @summary Test that call to canDecodeInput in ImageIO don't corrupt + * mark/reset stack in ImageInputStream + * @author Jiri Vanek + */ + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteOrder; +import java.util.Locale; +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.spi.IIORegistry; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.IIOByteBuffer; +import javax.imageio.stream.ImageInputStream; + + +public class MarkTryFinallyReproducer { + + private static final byte[] bmp = new byte[]{ + 127,127, 66, 77, -86, 0, 0, 0, 0, 0, 0, 0, + 122, 0, 0, 0, 108, 0, 0, 0, 4, 0, 0, 0, 4, + 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0, 48, 0, 0, + 0, 19, 11, 0, 0, 19, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 66, 71, 82, 115, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, -1, -1, + -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, -17, + 0, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, -1, -1, -1, + -1, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0, -1 + }; + //first two are evil, we are skipping them later. Others are normal BMP + + private static class NotClosingImageInputStream implements ImageInputStream { + + private final ImageInputStream src; + + private NotClosingImageInputStream(ImageInputStream createImageInputStream) { + this.src = createImageInputStream; + } + + @Override + public void setByteOrder(ByteOrder byteOrder) { + src.setByteOrder(byteOrder); + } + + @Override + public ByteOrder getByteOrder() { + return src.getByteOrder(); + } + + @Override + public int read() throws IOException { + return src.read(); + } + + @Override + public int read(byte[] b) throws IOException { + return src.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return src.read(b, off, len); + } + + @Override + public void readBytes(IIOByteBuffer buf, int len) throws IOException { + src.readBytes(buf, len); + } + + @Override + public boolean readBoolean() throws IOException { + return src.readBoolean(); + } + + @Override + public byte readByte() throws IOException { + return src.readByte(); + } + + @Override + public int readUnsignedByte() throws IOException { + return src.readUnsignedByte(); + } + + @Override + public short readShort() throws IOException { + return src.readShort(); + } + + @Override + public int readUnsignedShort() throws IOException { + return src.readUnsignedShort(); + } + + @Override + public char readChar() throws IOException { + return src.readChar(); + } + + @Override + public int readInt() throws IOException { + return src.readInt(); + } + + @Override + public long readUnsignedInt() throws IOException { + return src.readUnsignedInt(); + } + + @Override + public long readLong() throws IOException { + return src.readLong(); + } + + @Override + public float readFloat() throws IOException { + return src.readFloat(); + } + + @Override + public double readDouble() throws IOException { + return src.readDouble(); + } + + @Override + public String readLine() throws IOException { + return src.readLine(); + } + + @Override + public String readUTF() throws IOException { + return src.readUTF(); + } + + @Override + public void readFully(byte[] b, int off, int len) throws IOException { + src.readFully(b, off, len); + } + + @Override + public void readFully(byte[] b) throws IOException { + src.readFully(b); + } + + @Override + public void readFully(short[] s, int off, int len) throws IOException { + src.readFully(s, off, len); + } + + @Override + public void readFully(char[] c, int off, int len) throws IOException { + src.readFully(c, off, len); + } + + @Override + public void readFully(int[] i, int off, int len) throws IOException { + src.readFully(i, off, len); + } + + @Override + public void readFully(long[] l, int off, int len) throws IOException { + src.readFully(l, off, len); + } + + @Override + public void readFully(float[] f, int off, int len) throws IOException { + src.readFully(f, off, len); + } + + @Override + public void readFully(double[] d, int off, int len) throws IOException { + src.readFully(d, off, len); + } + + @Override + public long getStreamPosition() throws IOException { + return src.getStreamPosition(); + } + + @Override + public int getBitOffset() throws IOException { + return src.getBitOffset(); + } + + @Override + public void setBitOffset(int bitOffset) throws IOException { + src.setBitOffset(bitOffset); + } + + @Override + public int readBit() throws IOException { + return src.readBit(); + } + + @Override + public long readBits(int numBits) throws IOException { + return src.readBits(numBits); + } + + @Override + public long length() throws IOException { + return src.length(); + } + + @Override + public int skipBytes(int n) throws IOException { + return src.skipBytes(n); + } + + @Override + public long skipBytes(long n) throws IOException { + return src.skipBytes(n); + } + + @Override + public void seek(long pos) throws IOException { + src.seek(pos); + } + + @Override + public void mark() { + src.mark(); + } + + @Override + public void reset() throws IOException { + src.reset(); + } + + @Override + public void flushBefore(long pos) throws IOException { + src.flushBefore(pos); + } + + @Override + public void flush() throws IOException { + src.flush(); + } + + @Override + public long getFlushedPosition() { + return src.getFlushedPosition(); + } + + @Override + public boolean isCached() { + return src.isCached(); + } + + @Override + public boolean isCachedMemory() { + return src.isCachedMemory(); + } + + @Override + public boolean isCachedFile() { + return src.isCachedFile(); + } + + @Override + public void close() throws IOException { + //the only important one. nothing + } + } + + static final String readerClassName + = MarkTryFinallyReproducerSpi.class.getName(); + static final String[] localNames = {"myNames"}; + static final String[] localSuffixes = {"mySuffixes"}; + static final String[] localMIMETypes = {"myMimes"}; + + public static class MarkTryFinallyReproducerSpi extends ImageReaderSpi { + + public MarkTryFinallyReproducerSpi() { + super("MarkTryFinallyReproducerSpi", + "1.0", + localNames, + localSuffixes, + localMIMETypes, + readerClassName, + new Class[]{ImageInputStream.class}, + new String[0], + false, + null, + null, + new String[0], + new String[0], + false, + null, + null, + new String[0], + new String[0]); + } + + @Override + public String getDescription(Locale locale) { + return ""; + } + + @Override + public boolean canDecodeInput(Object input) throws IOException { + throw new IOException("Bad luck"); + } + + @Override + public ImageReader createReaderInstance(Object extension) { + return null; + } + } + + public static void main(String[] args) throws IOException { + MarkTryFinallyReproducerSpi spi = new MarkTryFinallyReproducerSpi(); + IIORegistry.getDefaultInstance().registerServiceProvider(spi); + + ImageInputStream iis1 = + new NotClosingImageInputStream(ImageIO.createImageInputStream(new ByteArrayInputStream(bmp))); + iis1.readByte(); + iis1.mark(); + long p1 = iis1.getStreamPosition(); + iis1.readByte(); + iis1.mark(); + long p2 = iis1.getStreamPosition(); + BufferedImage bi1 = ImageIO.read(iis1); + iis1.reset(); + long pn2 = iis1.getStreamPosition(); + iis1.reset(); + long pn1 = iis1.getStreamPosition(); + if (p1 != pn1 || p2!= pn2) { + throw new RuntimeException("Exception from call to canDecodeInput in ImageIO. " + + "Corrupted stack in ImageInputStream"); + } + + } + +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/MBeanServer/ExceptionFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/MBeanServer/ExceptionFactory.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.ArrayList; +import javax.management.AttributeNotFoundException; +import javax.management.BadAttributeValueExpException; +import javax.management.BadBinaryOpValueExpException; +import javax.management.BadStringOperationException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidApplicationException; +import javax.management.InvalidAttributeValueException; +import javax.management.JMException; +import javax.management.JMRuntimeException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanRegistrationException; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.OperationsException; +import javax.management.ReflectionException; +import javax.management.RuntimeErrorException; +import javax.management.RuntimeMBeanException; +import javax.management.RuntimeOperationsException; +import javax.management.ServiceNotFoundException; +import javax.management.StringValueExp; +import javax.management.modelmbean.InvalidTargetObjectTypeException; +import javax.management.modelmbean.XMLParseException; +import javax.management.monitor.MonitorSettingException; +import javax.management.openmbean.InvalidKeyException; +import javax.management.openmbean.InvalidOpenTypeException; +import javax.management.openmbean.KeyAlreadyExistsException; +import javax.management.openmbean.OpenDataException; +import javax.management.relation.InvalidRelationIdException; +import javax.management.relation.InvalidRelationServiceException; +import javax.management.relation.InvalidRelationTypeException; +import javax.management.relation.InvalidRoleInfoException; +import javax.management.relation.InvalidRoleValueException; +import javax.management.relation.RelationException; +import javax.management.relation.RelationNotFoundException; +import javax.management.relation.RelationServiceNotRegisteredException; +import javax.management.relation.RelationTypeNotFoundException; +import javax.management.relation.RoleInfoNotFoundException; +import javax.management.relation.RoleNotFoundException; +import javax.management.remote.JMXProviderException; +import javax.management.remote.JMXServerErrorException; + +/** + * |----- Original Description Coming From Tonga Original Source Code -------| + * | | + * | That class creates an ArrayList and fill it with an instance of each of | + * | the Exception class of the JMX API. | + * | It's dedicated to use by ExceptionTest. | + * |-------------------------------------------------------------------------| + */ +public class ExceptionFactory { + + public static final ArrayList exceptions = + new ArrayList(); + + static { + String mes = "SQE"; + exceptions.add(new AttributeNotFoundException()); + exceptions.add(new BadAttributeValueExpException(mes)); + exceptions.add(new BadBinaryOpValueExpException(new StringValueExp(mes))); + exceptions.add(new BadStringOperationException(mes)); + exceptions.add(new InstanceAlreadyExistsException()); + exceptions.add(new InstanceNotFoundException()); + exceptions.add(new IntrospectionException()); + exceptions.add(new InvalidApplicationException(mes)); + exceptions.add(new InvalidAttributeValueException()); + exceptions.add(new JMException()); + exceptions.add(new JMRuntimeException()); + exceptions.add(new ListenerNotFoundException()); + exceptions.add(new MalformedObjectNameException()); + exceptions.add(new MBeanException(new Exception(mes), mes)); + exceptions.add(new MBeanRegistrationException(new Exception(mes), mes)); + exceptions.add(new NotCompliantMBeanException()); + exceptions.add(new OperationsException()); + exceptions.add(new ReflectionException(new Exception(mes), mes)); + exceptions.add(new RuntimeErrorException(new Error(mes), mes)); + exceptions.add(new RuntimeMBeanException(new RuntimeException(mes), mes)); + exceptions.add(new RuntimeOperationsException(new RuntimeException(mes), mes)); + exceptions.add(new ServiceNotFoundException()); + exceptions.add(new InvalidTargetObjectTypeException()); + exceptions.add(new XMLParseException()); + exceptions.add(new MonitorSettingException()); + exceptions.add(new InvalidKeyException()); + exceptions.add(new InvalidOpenTypeException()); + exceptions.add(new KeyAlreadyExistsException()); + exceptions.add(new OpenDataException()); + exceptions.add(new InvalidRelationIdException()); + exceptions.add(new InvalidRelationServiceException()); + exceptions.add(new InvalidRelationTypeException()); + exceptions.add(new InvalidRoleInfoException()); + exceptions.add(new InvalidRoleValueException()); + exceptions.add(new RelationException()); + exceptions.add(new RelationNotFoundException()); + exceptions.add(new RelationServiceNotRegisteredException()); + exceptions.add(new RelationTypeNotFoundException()); + exceptions.add(new RoleInfoNotFoundException()); + exceptions.add(new RoleNotFoundException()); + exceptions.add(new JMXProviderException()); + exceptions.add(new JMXServerErrorException(mes, new Error(mes))); + ExceptionTest.Utils.debug(ExceptionTest.Utils.DEBUG_STANDARD, + "DataFactory::updateMap: Initialized" + + " an ArrayList with the " + + exceptions.size() + " exceptions of the JMX API"); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/MBeanServer/ExceptionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/MBeanServer/ExceptionTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8058865 + * @summary Checks that exceptions are correctly wired (compared to reference). + * @author Olivier Lagneau + * @modules java.management + * @run main/othervm/timeout=300 -DDEBUG_STANDARD ExceptionTest + */ + +import java.util.Map; +import java.util.HashMap; +import java.util.Properties; +import java.lang.reflect.Method; + +import java.lang.management.ManagementFactory; +import javax.management.ObjectName; +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; + + +public class ExceptionTest { + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + ExceptionTest test = new ExceptionTest(); + test.run(map); + + } + + public void run(Map args) { + + System.out.println("ExceptionTest::run: Start"); + int errorCount = 0; + + JMXConnectorServer cs = null; + JMXConnector cc = null; + + try { + // JMX MbeanServer used inside single VM as if remote. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // ---- + ObjectName objName = + new ObjectName(ExceptionThrower.EXCEPTION_THROWER_NAME); + System.out.println("ExceptionTest::run: Create and register MBean " + objName); + mbsc.createMBean("ExceptionThrower", objName); + System.out.println("---- OK\n"); + + // ---- + System.out.println("ExceptionTest::run: Ask for exception(s)"); + Object[] throwExceptionParam = new Object[1]; + String[] throwExceptionSig = new String[]{"int"}; + + for (int i = 0; i < ExceptionFactory.exceptions.size(); i++) { + throwExceptionParam[0] = new Integer(i); + + Exception ex = + (Exception)mbsc.invoke(objName, + "throwException", throwExceptionParam, throwExceptionSig); + + if ( ! matches(ex, ExceptionFactory.exceptions.get(i)) ) { + errorCount++; + System.out.println("ExceptionTest::run: (ERROR) Received \n[" + + ex + "]\nin place of\n[" + + ExceptionFactory.exceptions.get(i) + "]"); + } else { + System.out.println("OK [" + ex + "]"); + } + } + + System.out.println("---- DONE\n"); + + } catch (Exception e) { + Utils.printThrowable(e, true); + throw new RuntimeException(); + } finally { + try { + // Close JMX Connector Client + cc.close(); + // Stop connertor server + cs.stop(); + + } catch (Exception e) { + Utils.printThrowable(e, true); + throw new RuntimeException( + "Unable to either close connector client or stop connector server"); + } + } + + if (errorCount == 0) { + System.out.println("ExceptionTest::run: Done without any error"); + } else { + System.out.println("ExceptionTest::run: Done with " + errorCount + + " error(s)"); + throw new RuntimeException("errorCount = " + errorCount); + } + + System.out.println("ExceptionTest::run: Done"); + } + + // Check both Exception are identical. + // That means: + // - none is null. + // - they are of the same Class. + // - if their respective messages aren't null they're equal. + // - if the message of one is null the message of the other is null too. + private boolean matches(Exception ex, Exception refex) { + if ( ex == null || refex == null ) { + System.out.println("(ERROR) Called with one or more null parameter; check " + + ex + " against " + refex); + return false; + } + + String exClass = ex.getClass().getName(); + String refexClass = refex.getClass().getName(); + + if ( ! exClass.equals(refexClass) ) { + System.out.println("(ERROR) Class names don't match; check [" + + exClass + "] against [" + refexClass + "]"); + return false; + } + + String exMes = ex.getMessage(); + String refexMes = refex.getMessage(); + + if ( exMes != null && refexMes != null ) { + if ( ! exMes.equals(refexMes) ) { + System.out.println("(ERROR) Non null messages don't match; check [" + + exMes + "] against [" + refexMes + "]"); + return false; + } + } else if ( (exMes == null && refexMes != null) + || (exMes != null && refexMes == null) ) { + System.out.println("(ERROR) Messages don't match; check [" + exMes + + "] against [" + refexMes + "]"); + } + + return true; + } + + // Utility inner class coming from JMX Tonga test suite. + // Also used by ExceptionFactory. + static class Utils { + + // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property + static final String DEBUG_HEADER = "[debug] "; + + // DEBUG levels + static int selectedDebugLevel = 0; + static final int DEBUG_STANDARD = 1; + static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests + static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE; + + static void parseDebugProperties() { + int level = 0; + Properties p = System.getProperties(); + + // get selected levels + if (p.getProperty("DEBUG_STANDARD") != null) { + level |= DEBUG_STANDARD; + } + + if (p.getProperty("DEBUG_VERBOSE") != null) { + level |= DEBUG_VERBOSE; + } + + if (p.getProperty("DEBUG_ALL") != null) { + level |= DEBUG_ALL; + } + + selectedDebugLevel = level; + } + + /** + * Reproduces the original parsing and collection of test parameters + * from the DTonga JMX test suite. + * + * Collects passed args and returns them in a map(argname, value) structure, + * which will be then propagated as necessary to various called methods. + */ + static Map parseParameters(String args[]) + throws Exception { + debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start"); + HashMap map = new HashMap<>(); + + for ( int i = 0; i < args.length; i++ ) { + if ( args[i].trim().startsWith("-") ) { + if ((i+1) < args.length && !args[i+1].startsWith("-") ) { + debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with value " + + args[i+1]) ; + map.put(args[i].trim(), args[i+1].trim()) ; + } else if ((i+1) < args.length && args[i+1].startsWith("-") || + (i+1) == args.length ) { + debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with null value") ; + map.put(args[i].trim(), null) ; + } else { + System.out.println( + "TestRoot::parseParameters: (WARNING) not added in map = " + + args[i]) ; + } + } + } + + debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ; + return map ; + } + + /** + * This method is to be used in all tests to print anything + * that is temporary. + * Printing is done only when debug is activated by the property DEBUG. + * Printing depends also on the DEBUG_LEVEL property. + * Here it encapsulates a System.out.println. + */ + static void debug(int level, String line) { + if ((selectedDebugLevel & level) != 0) { + System.out.println(DEBUG_HEADER + line); + } + } + + /** + * Do print stack trace when withStack is true. + * Does try to call getTargetException() and getTargetError() then + * print embedded stacks in the case of an Exception wrapping + * another Exception or an Error. Recurse until no more wrapping + * is found. + */ + static void printThrowable(Throwable theThro, boolean withStack) { + try { + if (withStack) { + theThro.printStackTrace(System.out); + } + if (theThro instanceof Exception) { + Exception t = (Exception) theThro; + Method target = null; + String blank = " "; + try { + target = t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not + } + System.out.println(blank + t.getClass() + "==>" + t.getMessage()); + while (target != null) { + try { + t = (Exception) target.invoke(t, + (java.lang.Object[]) null); + } catch (Exception ee) { + t = null; + } + try { + if (t != null) { + blank = blank + " "; + System.out.println(blank + t.getClass() + "==>" + + t.getMessage()); + try { + target = + t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not } + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + + // We may have exceptions wrapping an Error then it is + // getTargetError that is likely to be called + try { + target = ((Exception) theThro).getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + Throwable err = theThro; + while (target != null) { + try { + err = (Error) target.invoke(err, + (java.lang.Object[]) null); + } catch (Exception ee) { + err = null; + } + try { + if (err != null) { + blank = blank + " "; + System.out.println(blank + err.getClass() + "==>" + + err.getMessage()); + if (withStack) { + err.printStackTrace(System.out); + } + try { + target = err.getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + } else { + System.out.println("Throwable is : " + theThro); + } + } catch (Throwable x) { + System.out.println("Exception : raised in printException : " + x); + } + } + } + +} + + diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/MBeanServer/ExceptionThrower.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/MBeanServer/ExceptionThrower.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 class defines a simple standard MBean. + */ +public class ExceptionThrower implements ExceptionThrowerMBean { + + public static final String EXCEPTION_THROWER_NAME + = "sqe:type=ExceptionThrower"; + + public Exception throwException(int exceptionIndex) { + return ExceptionFactory.exceptions.get(exceptionIndex); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/MBeanServer/ExceptionThrowerMBean.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/MBeanServer/ExceptionThrowerMBean.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 interface defines a simple standard MBean. + */ +public interface ExceptionThrowerMBean { + public Exception throwException(int exceptionIndex); +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/Basic.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/Basic.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import javax.management.Descriptor; +import javax.management.ImmutableDescriptor; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; + +/** + * Class Basic + * Basic Description + */ +public class Basic implements BasicMXBean, NotificationEmitter, + MBeanRegistration { + + public static final String EXCEPTION_MESSAGE = "from Basic"; + public static final String NOTIFICATION_MESSAGE = "from Basic"; + /** Attribute : IntAtt */ + private int intAtt = 0; + /** Attribute : IntegerAtt */ + private Integer integerAtt = 0; + /** Attribute : BoolAtt */ + private boolean boolAtt = false; + /** Attribute : BooleanAtt */ + private Boolean booleanAtt = false; + /** Attribute : StringAtt */ + private String stringAtt = null; + /** Attribute : DateAtt */ + private Date dateAtt = null; + /** Attribute : ObjectNameAtt */ + private ObjectName objectNameAtt = null; + /** Attribute : NotifDescriptorAsMapAtt */ + private Map notifDescriptorAsMapAtt = null; + /** Attribute : NotifDescriptorAtt */ + private Descriptor notifDescriptorAtt = null; + /** Attribute : SqeParameter */ + private SqeParameter sqeParameterAtt = null; + + /* Creates a new instance of Basic */ + @SqeDescriptorKey("CONSTRUCTOR Basic") + public Basic() { + } + + /* Creates a new instance of Basic */ + @SqeDescriptorKey("CONSTRUCTOR Basic") + public Basic( + @SqeDescriptorKey("CONSTRUCTOR PARAMETER SqeParameter") SqeParameter param) { + } + + /** + * Get int attribute + */ + public int getIntAtt() { + return intAtt; + } + + /** + * Set int attribute + */ + public void setIntAtt(int value) { + intAtt = value; + } + + /** + * Get Integer attribute + */ + public Integer getIntegerAtt() { + return integerAtt; + } + + /** + * Set Integer attribute + */ + public void setIntegerAtt(Integer value) { + integerAtt = value; + } + + /** + * Get boolean attribute + */ + public boolean getBoolAtt() { + return boolAtt; + } + + /** + * Set boolean attribute + */ + public void setBoolAtt(boolean value) { + boolAtt = value; + } + + /** + * Get Boolean attribute + */ + public Boolean getBooleanAtt() { + return booleanAtt; + } + + /** + * Set Boolean attribute + */ + public void setBooleanAtt(Boolean value) { + booleanAtt = value; + } + + /** + * Get String attribute + */ + public String getStringAtt() { + return stringAtt; + } + + /** + * Set String attribute + */ + public void setStringAtt(String value) { + stringAtt = value; + } + + /** + * Get Date attribute + */ + public Date getDateAtt() { + return dateAtt; + } + + /** + * Set Date attribute + */ + public void setDateAtt(Date value) { + dateAtt = value; + } + + /** + * Get ObjectName attribute + */ + public ObjectName getObjectNameAtt() { + return objectNameAtt; + } + + /** + * Set ObjectName attribute + */ + public void setObjectNameAtt(ObjectName value) { + objectNameAtt = value; + } + + /** + * Get SqeParameter attribute + */ + public SqeParameter getSqeParameterAtt() throws Exception { + if (sqeParameterAtt == null) { + sqeParameterAtt = new SqeParameter(); + sqeParameterAtt.setGlop("INITIALIZED"); + } + + return sqeParameterAtt; + } + + /** + * Set SqeParameter attribute + */ + public void setSqeParameterAtt(SqeParameter value) { + sqeParameterAtt = value; + } + + /** + * Get the Descriptor used to build the NotificationInfo + * of emitted notifications. + */ + public Map getNotifDescriptorAsMapAtt() { + if (notifDescriptorAsMapAtt == null) { + initNotifDescriptorAtt(); + } + + return notifDescriptorAsMapAtt; + } + + /** + * Set the Descriptor used to build the NotificationInfo + * of emitted notifications. + *
    A Map would better fit Descriptor needs but then + * it is not convertible according the MXBean specification so the MBean + * registration fails. + * As we plan to test our custom Descriptor finds its way into + * the metadata of emitted notifications, String is good enough. + */ + public void setNotifDescriptorAsMapAtt(Map value) { + notifDescriptorAsMapAtt = new HashMap(value); + notifDescriptorAtt = new ImmutableDescriptor(value); + } + + /** + * Do nothing + */ + public void doNothing() { + // I said NOTHING ! + } + + /** + * Do take SqeParameter as a parameter + */ + public void doWeird(SqeParameter param) { + } + + /** + * Throw an Exception + */ + public void throwException() throws Exception { + throw new Exception(EXCEPTION_MESSAGE); + } + + /** + * Throw an Error + */ + public void throwError() { + throw new InternalError(EXCEPTION_MESSAGE); + } + + /** + * Reset all attributes + */ + public void reset() { + intAtt = 0; + integerAtt = 0; + boolAtt = false; + booleanAtt = Boolean.FALSE; + stringAtt = null; + dateAtt = null; + objectNameAtt = null; + } + + /** + * Returns the weather for the coming days + * @param verbose boolean verbosity + * @throws java.lang.Exception storm + * @return ObjectName + */ + public Weather getWeather(boolean verbose) + throws java.lang.Exception { + return Weather.SUNNY; + } + + // Starting here are the 4 methods of MBeanRegistration interface. + // We use that to grab the ObjectName the MBean is registered with. + // + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + // Grab a reference on the MBeanServer we're registered in. + mbs = server; + // Compute the name we're registered with. + if (name != null) { + mbeanName = name; + return name; + } else { + mbeanName = + new ObjectName("sqe:type=" + Basic.class.getName()); + return mbeanName; + } + } + + public void postRegister(Boolean registrationDone) { + // Do nothing + } + + public void preDeregister() throws Exception { + // Do nothing + } + + public void postDeregister() { + // Do nothing + } + + /** + * Send one Notification of the provided notifType type. + */ + public void sendNotification(String notifType) { + Notification notification = null; + + if (notifType.equals(NOTIF_TYPE_0)) { + notification = new Notification(NOTIF_TYPE_0, + mbeanName, + seqNumber, + NOTIFICATION_MESSAGE); + } else if (notifType.equals(NOTIF_TYPE_1)) { + notification = new SqeNotification(NOTIF_TYPE_1, + mbeanName, + seqNumber, + NOTIFICATION_MESSAGE); + } + + seqNumber++; + broadcaster.sendNotification(notification); + } + + /** + * That method starts a set of threads, each thread sends a given number of + * notifications. + * The number of threads can be set via the attribute numOfNotificationSenders. + * The number of notification sent by each thread can be set via + * the attribute numOfNotificationSenderLoops. + * Depending on the parameter customNotification we send either custom + * notification(s) or MBeanServer registration and unregistration notification(s). + * When customNotification=true the total number of notification(s) sent is + * (numOfNotificationSenders * numOfNotificationSenderLoops). They are + * sequentially of type NOTIF_TYPE_0 then NOTIF_TYPE_1 and so on. + * + * When customNotification=false the total number of notification(s) sent is + * (numOfNotificationSenders * numOfNotificationSenderLoops) registration + * notification(s) + * + + * (numOfNotificationSenders * numOfNotificationSenderLoops) unregistration + * notification(s) + * + * @throws java.lang.Exception + */ + public void sendNotificationWave(boolean customNotification) throws + Exception { + // Build the set of notification sender. + Collection> tasks = + new HashSet>(numOfNotificationSenders); + + for (int i = 1; i <= numOfNotificationSenders; i++) { + tasks.add(new NotifSender(numOfNotificationSenderLoops, + customNotification, i)); + } + + // Start all notification sender in parallel. + ExecutorService execServ = null; + try { + execServ = Executors.newFixedThreadPool(numOfNotificationSenders); + List> taskHandlers = execServ.invokeAll(tasks); + checkNotifSenderThreadStatus(taskHandlers); + } finally { + if (!execServ.isShutdown()) { + execServ.shutdown(); + } + } + } + + public void setNumOfNotificationSenders(int value) { + numOfNotificationSenders = value; + } + + public void setNumOfNotificationSenderLoops(int value) { + numOfNotificationSenderLoops = value; + } + + /** + * MBean Notification support + * You shouldn't update these methods + */ + // + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) + throws IllegalArgumentException { + broadcaster.addNotificationListener(listener, filter, handback); + } + + public MBeanNotificationInfo[] getNotificationInfo() { + if (notifDescriptorAtt == null) { + initNotifDescriptorAtt(); + } + + return new MBeanNotificationInfo[]{ + new MBeanNotificationInfo(new String[]{ + NOTIF_TYPE_0 + }, + javax.management.Notification.class.getName(), + "Standard JMX Notification", + notifDescriptorAtt), + new MBeanNotificationInfo(new String[]{ + NOTIF_TYPE_1 + }, + SqeNotification.class.getName(), + "SQE Notification", + notifDescriptorAtt) + }; + } + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener); + } + + public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener, filter, handback); + } + // + private synchronized long getNextSeqNumber() { + return seqNumber++; + } + + private void initNotifDescriptorAtt() { + String key = "CRABE"; + String value = "TAMBOUR"; + notifDescriptorAtt = + new ImmutableDescriptor(new String[]{key + "=" + value}); + notifDescriptorAsMapAtt = + new HashMap(); + notifDescriptorAsMapAtt.put(key, value); + } + + private void checkNotifSenderThreadStatus( + List> taskHandlers) + throws Exception { + String msgTag = "Basic::checkNotifSenderThreadStatus: "; + // Grab back status of each notification sender. + for (Future f : taskHandlers) { + if (f.isCancelled()) { + String message = msgTag + + "---- ERROR : One thread has been cancelled"; + System.out.println(message); + throw new RuntimeException(message); + } else { + Integer effectiveNumOfLoops = f.get(); + + if (effectiveNumOfLoops != numOfNotificationSenderLoops) { + String message = msgTag + "---- ERROR : One thread did " + + effectiveNumOfLoops + " loops in place of " + + numOfNotificationSenderLoops; + System.out.println(message); + throw new RuntimeException(message); + } + } + } + } + // + private int numOfNotificationSenderLoops = 2; + private int numOfNotificationSenders = 13; + + private class NotifSender implements Callable { + + private int cycles; + private boolean customNotification; + private int senderID; + + public NotifSender(int cycles, boolean customNotification, int id) { + this.cycles = cycles; + this.customNotification = customNotification; + this.senderID = id; + } + + public Integer call() throws Exception { + int callsDone = 0; + + try { + for (int i = 1; i <= cycles; i++) { + if (customNotification) { + if (i % 2 == 0) { + sendNotification(NOTIF_TYPE_0); + } else { + sendNotification(NOTIF_TYPE_1); + } + } else { + ObjectName mbeanName = new ObjectName("SQE:type=" + + mbeanClassName + ",senderID=" + senderID); + mbs.createMBean(mbeanClassName, mbeanName); + mbs.unregisterMBean(mbeanName); + } + callsDone++; + } + } catch (Exception e) { + System.out.println("NotifSender::call: (ERROR) Thread [" + senderID + + "] failed after " + callsDone + " cycles"); + throw e; + } + + return Integer.valueOf(callsDone); + } + } + + // + private long seqNumber; + private final NotificationBroadcasterSupport broadcaster = + new NotificationBroadcasterSupport(); + private ObjectName mbeanName; + private MBeanServer mbs; + private String mbeanClassName = "Simple"; + + /** + * Notification types definitions. To use when creating JMX Notifications. + */ + public static final String NOTIF_TYPE_0 = + "sqe.notification.a.type"; + public static final String NOTIF_TYPE_1 = + "sqe.notification.b.type"; +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/BasicMXBean.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/BasicMXBean.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Date; +import java.util.Map; + +import javax.management.ObjectName; + +/** + * Interface BasicMBean + * Basic Description + */ +@SqeDescriptorKey("INTERFACE BasicMXBean") +public interface BasicMXBean +{ + /** + * Get int attribute + */ + @SqeDescriptorKey("ATTRIBUTE intAtt") + public int getIntAtt(); + + /** + * Set int attribute + */ + @SqeDescriptorKey("ATTRIBUTE intAtt") + public void setIntAtt(int value); + + /** + * Get Integer attribute + */ + @SqeDescriptorKey("ATTRIBUTE integerAtt") + public Integer getIntegerAtt(); + + /** + * Set Integer attribute + */ + @SqeDescriptorKey("ATTRIBUTE integerAtt") + public void setIntegerAtt(Integer value); + + /** + * Get boolean attribute + */ + @SqeDescriptorKey("ATTRIBUTE boolAtt") + public boolean getBoolAtt(); + + /** + * Set boolean attribute + */ + @SqeDescriptorKey("ATTRIBUTE boolAtt") + public void setBoolAtt(boolean value); + + /** + * Get Boolean attribute + */ + @SqeDescriptorKey("ATTRIBUTE booleanAtt") + public Boolean getBooleanAtt(); + + /** + * Set Boolean attribute + */ + @SqeDescriptorKey("ATTRIBUTE booleanAtt") + public void setBooleanAtt(Boolean value); + + /** + * Get String attribute + */ + @SqeDescriptorKey("ATTRIBUTE stringAtt") + public String getStringAtt(); + + /** + * Set String attribute + */ + @SqeDescriptorKey("ATTRIBUTE stringAtt") + public void setStringAtt(String value); + + /** + * Get Date attribute + */ + @SqeDescriptorKey("ATTRIBUTE dateAtt") + public Date getDateAtt(); + + /** + * Set Date attribute + */ + @SqeDescriptorKey("ATTRIBUTE dateAtt") + public void setDateAtt(Date value); + + /** + * Get ObjectName attribute + */ + @SqeDescriptorKey("ATTRIBUTE objectNameAtt") + public ObjectName getObjectNameAtt(); + + /** + * Set ObjectName attribute + */ + @SqeDescriptorKey("ATTRIBUTE objectNameAtt") + public void setObjectNameAtt(ObjectName value); + + /** + * Get SqeParameter attribute + */ + @SqeDescriptorKey("ATTRIBUTE sqeParameterAtt") + public SqeParameter getSqeParameterAtt() throws Exception; + + /** + * Set SqeParameter attribute + */ + @SqeDescriptorKey("ATTRIBUTE sqeParameterAtt") + public void setSqeParameterAtt(SqeParameter value); + + /** + * Set NumOfNotificationSenders attribute + */ + @SqeDescriptorKey("ATTRIBUTE NumOfNotificationSenders") + public void setNumOfNotificationSenders(int value); + + /** + * Set NumOfNotificationSenderLoops attribute + */ + @SqeDescriptorKey("ATTRIBUTE NumOfNotificationSenderLoops") + public void setNumOfNotificationSenderLoops(int value); + + /** + * do nothing + * + */ + @SqeDescriptorKey("OPERATION doNothing") + public void doNothing(); + + /** + * Do take SqeParameter as a parameter + */ + @SqeDescriptorKey("OPERATION doWeird") + public void doWeird(@SqeDescriptorKey("METHOD PARAMETER")SqeParameter param); + + /** + * throw an Exception + * + */ + @SqeDescriptorKey("OPERATION throwException") + public void throwException() throws Exception; + + /** + * throw an Error + * + */ + @SqeDescriptorKey("OPERATION throwError") + public void throwError(); + + /** + * reset all attributes + * + */ + @SqeDescriptorKey("OPERATION reset") + public void reset(); + + /** + * returns the weather for the coming days + * + * @param verbose boolean verbosity + * @return ObjectName + */ + @SqeDescriptorKey("OPERATION getWeather") + public Weather getWeather(@SqeDescriptorKey("METHOD PARAMETER")boolean verbose) + throws java.lang.Exception; + + public enum Weather { + CLOUDY, SUNNY + } + + @SqeDescriptorKey("ATTRIBUTE notifDescriptorAsMapAtt") + public Map getNotifDescriptorAsMapAtt(); + + @SqeDescriptorKey("ATTRIBUTE notifDescriptorAsMapAtt") + public void setNotifDescriptorAsMapAtt(Map value); + + @SqeDescriptorKey("OPERATION sendNotification") + public void sendNotification(@SqeDescriptorKey("METHOD PARAMETER")String notifType); + + @SqeDescriptorKey("OPERATION sendNotificationWave") + public void sendNotificationWave(boolean customNotification) throws Exception; +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/MXBeanExceptionHandlingTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/MXBeanExceptionHandlingTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8058865 + * @summary Checks correct exception and error events from NotificationListener + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile Basic.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanExceptionHandlingTest -timeForNotificationInSeconds 3 + */ + + +import java.util.Map; +import java.util.HashMap; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +import java.lang.management.ManagementFactory; +import javax.management.MBeanServer; +import javax.management.MBeanException; +import javax.management.MBeanServerDelegate; +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.RuntimeErrorException; +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; + +public class MXBeanExceptionHandlingTest implements NotificationListener { + + private static String BASIC_MXBEAN_CLASS_NAME = "Basic"; + + private long timeForNotificationInSeconds = 3L; + private int numOfNotifications = 2; + private BlockingQueue notifList = null; + + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + MXBeanExceptionHandlingTest test = new MXBeanExceptionHandlingTest(); + test.run(map); + + } + + protected void parseArgs(Map args) throws Exception { + + String arg = null; + + // Init timeForNotificationInSeconds + // It is the maximum time in seconds we wait for a notification. + arg = (String)args.get("-timeForNotificationInSeconds") ; + if (arg != null) { + timeForNotificationInSeconds = (new Long(arg)).longValue(); + } + + } + + public void run(Map args) { + + System.out.println("MXBeanExceptionHandlingTest::run: Start") ; + int errorCount = 0 ; + + try { + parseArgs(args); + notifList = new ArrayBlockingQueue(numOfNotifications); + + // JMX MbeanServer used inside single VM as if remote. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // ---- + System.out.println("Add me as notification listener"); + mbsc.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, + this, null, null); + System.out.println("---- OK\n") ; + + // ---- + System.out.println("Create and register the MBean"); + ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ; + mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName); + System.out.println("---- OK\n") ; + + // ---- + System.out.println("Call method throwException on our MXBean"); + + try { + mbsc.invoke(objName, "throwException", null, null); + errorCount++; + System.out.println("(ERROR) Did not get awaited MBeanException") ; + } catch (MBeanException mbe) { + System.out.println("(OK) Got awaited MBeanException") ; + Throwable cause = mbe.getCause(); + + if ( cause instanceof java.lang.Exception ) { + System.out.println("(OK) Cause is of the right class") ; + String mess = cause.getMessage(); + + if ( mess.equals(Basic.EXCEPTION_MESSAGE ) ) { + System.out.println("(OK) Cause message is fine") ; + } else { + errorCount++; + System.out.println("(ERROR) Cause has message " + + cause.getMessage() + + " as we expect " + + Basic.EXCEPTION_MESSAGE) ; + } + } else { + errorCount++; + System.out.println("(ERROR) Cause is of class " + + cause.getClass().getName() + + " as we expect java.lang.Exception") ; + } + } catch (Exception e) { + errorCount++; + System.out.println("(ERROR) Did not get awaited MBeanException but " + + e) ; + Utils.printThrowable(e, true); + } + System.out.println("---- DONE\n") ; + + // ---- + System.out.println("Call method throwError on our MXBean"); + + try { + mbsc.invoke(objName, "throwError", null, null); + errorCount++; + System.out.println("(ERROR) Did not get awaited RuntimeErrorException") ; + } catch (RuntimeErrorException ree) { + System.out.println("(OK) Got awaited RuntimeErrorException") ; + Throwable cause = ree.getCause(); + + if ( cause instanceof java.lang.InternalError ) { + System.out.println("(OK) Cause is of the right class") ; + String mess = cause.getMessage(); + + if ( mess.equals(Basic.EXCEPTION_MESSAGE ) ) { + System.out.println("(OK) Cause message is fine") ; + } else { + errorCount++; + System.out.println("(ERROR) Cause has message " + + cause.getMessage() + + " as we expect " + + Basic.EXCEPTION_MESSAGE) ; + } + } else { + errorCount++; + System.out.println("(ERROR) Cause is of class " + + cause.getClass().getName() + + " as we expect java.lang.InternalError") ; + } + } catch (Exception e) { + errorCount++; + System.out.println("(ERROR) Did not get awaited RuntimeErrorException but " + + e) ; + Utils.printThrowable(e, true); + } + System.out.println("---- DONE\n") ; + + // ---- + System.out.println("Unregister the MBean"); + mbsc.unregisterMBean(objName); + System.out.println("---- OK\n") ; + + Thread.sleep(timeForNotificationInSeconds * 1000); + int numOfReceivedNotif = notifList.size(); + + if ( numOfReceivedNotif == numOfNotifications ) { + System.out.println("(OK) We received " + + numOfNotifications + + " Notifications") ; + } else { + errorCount++; + System.out.println("(ERROR) We received " + + numOfReceivedNotif + + " Notifications in place of " + + numOfNotifications) ; + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + throw new RuntimeException(e); + } + + if ( errorCount == 0 ) { + System.out.println("MXBeanExceptionHandlingTest::run: Done without any error") ; + } else { + System.out.println("MXBeanExceptionHandlingTest::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + } + + public void handleNotification(Notification notification, Object handback) { + System.out.println("MXBeanExceptionHandlingTest::handleNotification: Received " + + notification); + notifList.add(notification); + } + +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/MXBeanInteropTest1.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/MXBeanInteropTest1.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,638 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8058865 + * @summary Test all MXBeans available by default on the platform + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanInteropTest1 + */ + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import java.lang.management.ClassLoadingMXBean; +import java.lang.management.CompilationMXBean; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryManagerMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadMXBean; + +import javax.management.JMX; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +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; + +public class MXBeanInteropTest1 { + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + MXBeanInteropTest1 test = new MXBeanInteropTest1(); + test.run(map); + + } + + public void run(Map args) { + + System.out.println("MXBeanInteropTest1::run: Start") ; + int errorCount = 0 ; + + try { + // JMX MbeanServer used inside single VM as if remote. + // MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // Print out registered java.lang.management MXBeans found + // in the remote jvm. + printMBeans(mbsc) ; + + // For each possible kind of JDK 5 defined MXBean, we retrieve its + // MBeanInfo and print it and we call all getters and print + // their output. + errorCount += doClassLoadingMXBeanTest(mbsc) ; + errorCount += doMemoryMXBeanTest(mbsc) ; + errorCount += doThreadMXBeanTest(mbsc) ; + errorCount += doRuntimeMXBeanTest(mbsc) ; + errorCount += doOperatingSystemMXBeanTest(mbsc) ; + errorCount += doCompilationMXBeanTest(mbsc) ; + errorCount += doGarbageCollectorMXBeanTest(mbsc) ; + errorCount += doMemoryManagerMXBeanTest(mbsc) ; + errorCount += doMemoryPoolMXBeanTest(mbsc) ; + + // Terminate the JMX Client + cc.close(); + + } catch(Exception e) { + Utils.printThrowable(e, true) ; + throw new RuntimeException(e); + } + + if ( errorCount == 0 ) { + System.out.println("MXBeanInteropTest1::run: Done without any error") ; + } else { + System.out.println("MXBeanInteropTest1::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + } + + /** + * Prints all MBeans of domain java.lang. + * They are MBeans related to the JSR 174 that defines + * package java.lang.management. + */ + private static void printMBeans(MBeanServerConnection mbsc) throws Exception { + ObjectName filterName = new ObjectName("java.lang:*"); + Set set = mbsc.queryNames(filterName, null); + + if ( set.size() == 0 ) { + throw new RuntimeException("(ERROR) No MBean found with filter " + + filterName); + } + + System.out.println("---- MBeans found in domain java.lang :"); + + for (Iterator iter = set.iterator(); iter.hasNext(); ) { + System.out.println(iter.next().toString()); + } + + System.out.println("\n") ; + } + + + private final int doClassLoadingMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- ClassLoadingMXBean") ; + + try { + ObjectName classLoadingName = + new ObjectName(ManagementFactory.CLASS_LOADING_MXBEAN_NAME) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(classLoadingName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + + mbInfo); + ClassLoadingMXBean classLoading = null; + + classLoading = JMX.newMXBeanProxy(mbsc, + classLoadingName, + ClassLoadingMXBean.class) ; + System.out.println("getLoadedClassCount\t\t" + + classLoading.getLoadedClassCount()); + System.out.println("getTotalLoadedClassCount\t\t" + + classLoading.getTotalLoadedClassCount()); + System.out.println("getUnloadedClassCount\t\t" + + classLoading.getUnloadedClassCount()); + System.out.println("isVerbose\t\t" + + classLoading.isVerbose()); + + System.out.println("---- OK\n") ; + + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doMemoryMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- MemoryMXBean") ; + + try { + ObjectName memoryName = + new ObjectName(ManagementFactory.MEMORY_MXBEAN_NAME) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(memoryName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + + mbInfo); + MemoryMXBean memory = null ; + + memory = + JMX.newMXBeanProxy(mbsc, + memoryName, + MemoryMXBean.class, + true) ; + System.out.println("getMemoryHeapUsage\t\t" + + memory.getHeapMemoryUsage()); + System.out.println("getNonHeapMemoryHeapUsage\t\t" + + memory.getNonHeapMemoryUsage()); + System.out.println("getObjectPendingFinalizationCount\t\t" + + memory.getObjectPendingFinalizationCount()); + System.out.println("isVerbose\t\t" + + memory.isVerbose()); + + System.out.println("---- OK\n") ; + + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doThreadMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- ThreadMXBean") ; + + try { + ObjectName threadName = + new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(threadName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + ThreadMXBean thread = null ; + + thread = + JMX.newMXBeanProxy(mbsc, + threadName, + ThreadMXBean.class) ; + System.out.println("findMonitorDeadlockedThreads\t\t" + + thread.findMonitorDeadlockedThreads()); + long[] threadIDs = thread.getAllThreadIds() ; + System.out.println("getAllThreadIds\t\t" + + threadIDs); + + for ( long threadID : threadIDs ) { + System.out.println("getThreadInfo long\t\t" + + thread.getThreadInfo(threadID)); + System.out.println("getThreadInfo long, int\t\t" + + thread.getThreadInfo(threadID, 2)); + } + + System.out.println("getThreadInfo long[]\t\t" + + thread.getThreadInfo(threadIDs)); + System.out.println("getThreadInfo long[], int\t\t" + + thread.getThreadInfo(threadIDs, 2)); + System.out.println("getDaemonThreadCount\t\t" + + thread.getDaemonThreadCount()); + System.out.println("getPeakThreadCount\t\t" + + thread.getPeakThreadCount()); + System.out.println("getThreadCount\t\t" + + thread.getThreadCount()); + System.out.println("getTotalStartedThreadCount\t\t" + + thread.getTotalStartedThreadCount()); + boolean supported = thread.isThreadContentionMonitoringSupported() ; + System.out.println("isThreadContentionMonitoringSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("isThreadContentionMonitoringEnabled\t\t" + + thread.isThreadContentionMonitoringEnabled()); + } + + supported = thread.isThreadCpuTimeSupported() ; + System.out.println("isThreadCpuTimeSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("isThreadCpuTimeEnabled\t\t" + + thread.isThreadCpuTimeEnabled()); + + for (long id : threadIDs) { + System.out.println("getThreadCpuTime(" + id + ")\t\t" + + thread.getThreadCpuTime(id)); + System.out.println("getThreadUserTime(" + id + ")\t\t" + + thread.getThreadUserTime(id)); + } + } + + supported = thread.isCurrentThreadCpuTimeSupported() ; + System.out.println("isCurrentThreadCpuTimeSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("getCurrentThreadCpuTime\t\t" + + thread.getCurrentThreadCpuTime()); + System.out.println("getCurrentThreadUserTime\t\t" + + thread.getCurrentThreadUserTime()); + } + + thread.resetPeakThreadCount() ; + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doRuntimeMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- RuntimeMXBean") ; + + try { + ObjectName runtimeName = + new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(runtimeName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + RuntimeMXBean runtime = null; + + runtime = + JMX.newMXBeanProxy(mbsc, + runtimeName, + RuntimeMXBean.class) ; + System.out.println("getClassPath\t\t" + + runtime.getClassPath()); + System.out.println("getInputArguments\t\t" + + runtime.getInputArguments()); + System.out.println("getLibraryPath\t\t" + + runtime.getLibraryPath()); + System.out.println("getManagementSpecVersion\t\t" + + runtime.getManagementSpecVersion()); + System.out.println("getName\t\t" + + runtime.getName()); + System.out.println("getSpecName\t\t" + + runtime.getSpecName()); + System.out.println("getSpecVendor\t\t" + + runtime.getSpecVendor()); + System.out.println("getSpecVersion\t\t" + + runtime.getSpecVersion()); + System.out.println("getStartTime\t\t" + + runtime.getStartTime()); + System.out.println("getSystemProperties\t\t" + + runtime.getSystemProperties()); + System.out.println("getUptime\t\t" + + runtime.getUptime()); + System.out.println("getVmName\t\t" + + runtime.getVmName()); + System.out.println("getVmVendor\t\t" + + runtime.getVmVendor()); + System.out.println("getVmVersion\t\t" + + runtime.getVmVersion()); + boolean supported = runtime.isBootClassPathSupported() ; + System.out.println("isBootClassPathSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("getBootClassPath\t\t" + + runtime.getBootClassPath()); + } + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doOperatingSystemMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- OperatingSystemMXBean") ; + + try { + ObjectName operationName = + new ObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(operationName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + OperatingSystemMXBean operation = null ; + + operation = + JMX.newMXBeanProxy(mbsc, + operationName, + OperatingSystemMXBean.class) ; + System.out.println("getArch\t\t" + + operation.getArch()); + System.out.println("getAvailableProcessors\t\t" + + operation.getAvailableProcessors()); + System.out.println("getName\t\t" + + operation.getName()); + System.out.println("getVersion\t\t" + + operation.getVersion()); + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doCompilationMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- CompilationMXBean") ; + + try { + ObjectName compilationName = + new ObjectName(ManagementFactory.COMPILATION_MXBEAN_NAME); + + if ( mbsc.isRegistered(compilationName) ) { + MBeanInfo mbInfo = mbsc.getMBeanInfo(compilationName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + CompilationMXBean compilation = null ; + + compilation = + JMX.newMXBeanProxy(mbsc, + compilationName, + CompilationMXBean.class) ; + System.out.println("getName\t\t" + + compilation.getName()); + boolean supported = + compilation.isCompilationTimeMonitoringSupported() ; + System.out.println("isCompilationTimeMonitoringSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("getTotalCompilationTime\t\t" + + compilation.getTotalCompilationTime()); + } + } + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doGarbageCollectorMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- GarbageCollectorMXBean") ; + + try { + ObjectName filterName = + new ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + + ",*"); + Set onSet = mbsc.queryNames(filterName, null); + + for (Iterator iter = onSet.iterator(); iter.hasNext(); ) { + ObjectName garbageName = iter.next() ; + System.out.println("-------- " + garbageName) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(garbageName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + GarbageCollectorMXBean garbage = null ; + + garbage = + JMX.newMXBeanProxy(mbsc, + garbageName, + GarbageCollectorMXBean.class) ; + System.out.println("getCollectionCount\t\t" + + garbage.getCollectionCount()); + System.out.println("getCollectionTime\t\t" + + garbage.getCollectionTime()); + } + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doMemoryManagerMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- MemoryManagerMXBean") ; + + try { + ObjectName filterName = + new ObjectName(ManagementFactory.MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE + + ",*"); + Set onSet = mbsc.queryNames(filterName, null); + + for (Iterator iter = onSet.iterator(); iter.hasNext(); ) { + ObjectName memoryManagerName = iter.next() ; + System.out.println("-------- " + memoryManagerName) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(memoryManagerName); + System.out.println("getMBeanInfo\t\t" + mbInfo); + errorCount += checkNonEmpty(mbInfo); + MemoryManagerMXBean memoryManager = null; + + memoryManager = + JMX.newMXBeanProxy(mbsc, + memoryManagerName, + MemoryManagerMXBean.class) ; + System.out.println("getMemoryPoolNames\t\t" + + Arrays.deepToString(memoryManager.getMemoryPoolNames())); + System.out.println("getName\t\t" + + memoryManager.getName()); + System.out.println("isValid\t\t" + + memoryManager.isValid()); + } + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doMemoryPoolMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- MemoryPoolMXBean") ; + + try { + ObjectName filterName = + new ObjectName(ManagementFactory.MEMORY_POOL_MXBEAN_DOMAIN_TYPE + + ",*"); + Set onSet = mbsc.queryNames(filterName, null); + + for (Iterator iter = onSet.iterator(); iter.hasNext(); ) { + ObjectName memoryPoolName = iter.next() ; + System.out.println("-------- " + memoryPoolName) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(memoryPoolName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + MemoryPoolMXBean memoryPool = null; + + memoryPool = + JMX.newMXBeanProxy(mbsc, + memoryPoolName, + MemoryPoolMXBean.class, + true) ; + System.out.println("getCollectionUsage\t\t" + + memoryPool.getCollectionUsage()); + System.out.println("getMemoryManagerNames\t\t" + + Arrays.deepToString(memoryPool.getMemoryManagerNames())); + System.out.println("getName\t\t" + + memoryPool.getName()); + System.out.println("getPeakUsage\t\t" + + memoryPool.getPeakUsage()); + System.out.println("getType\t\t" + + memoryPool.getType()); + System.out.println("getUsage\t\t" + + memoryPool.getUsage()); + System.out.println("isValid\t\t" + + memoryPool.isValid()); + boolean supported = memoryPool.isUsageThresholdSupported() ; + System.out.println("isUsageThresholdSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("getUsageThreshold\t\t" + + memoryPool.getUsageThreshold()); + System.out.println("isUsageThresholdExceeded\t\t" + + memoryPool.isUsageThresholdExceeded()); + System.out.println("getUsageThresholdCount\t\t" + + memoryPool.getUsageThresholdCount()); + } + + supported = memoryPool.isCollectionUsageThresholdSupported() ; + System.out.println("isCollectionUsageThresholdSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("getCollectionUsageThreshold\t\t" + + memoryPool.getCollectionUsageThreshold()); + System.out.println("getCollectionUsageThresholdCount\t\t" + + memoryPool.getCollectionUsageThresholdCount()); + System.out.println("isCollectionUsageThresholdExceeded\t\t" + + memoryPool.isCollectionUsageThresholdExceeded()); + } + + memoryPool.resetPeakUsage(); + } + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private int checkNonEmpty(MBeanInfo mbi) { + if ( mbi.toString().length() == 0 ) { + System.out.println("(ERROR) MBeanInfo is empty !"); + return 1; + } else { + return 0; + } + } + +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/MXBeanInteropTest2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/MXBeanInteropTest2.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8058865 + * @summary Checks access to test MXBean + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile Basic.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanInteropTest2 + */ + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.JMX; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanServer; +import java.lang.management.ManagementFactory; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +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; + +public class MXBeanInteropTest2 { + + private static String BASIC_MXBEAN_CLASS_NAME = "Basic"; + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + MXBeanInteropTest2 test = new MXBeanInteropTest2(); + test.run(map); + + } + + public void run(Map args) { + + System.out.println("MXBeanInteropTest2::run: Start") ; + int errorCount = 0 ; + + try { + // JMX MbeanServer used inside single VM as if remote. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // Prints all MBeans whatever the domain is. + printMBeans(mbsc) ; + + // Call test body + errorCount += doBasicMXBeanTest(mbsc) ; + + // Terminate the JMX Client + cc.close(); + + } catch(Exception e) { + Utils.printThrowable(e, true) ; + throw new RuntimeException(e); + } + + if ( errorCount == 0 ) { + System.out.println("MXBeanInteropTest2::run: Done without any error") ; + } else { + System.out.println("MXBeanInteropTest2::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + } + + + /** + * Prints all MBeans whatever the domain is. + */ + private static void printMBeans(MBeanServerConnection mbsc) throws Exception { + Set set = mbsc.queryNames(null, null); + System.out.println("---- MBeans found :"); + + for (Iterator iter = set.iterator(); iter.hasNext(); ) { + System.out.println(iter.next().toString()); + } + + System.out.println("\n") ; + } + + + private final int doBasicMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- doBasicMXBeanTest") ; + + try { + ObjectName objName = + new ObjectName("sqe:type=BasicMXBean") ; + mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName); + MBeanInfo mbInfo = mbsc.getMBeanInfo(objName); + printMBeanInfo(mbInfo); + System.out.println("---- OK\n") ; + System.out.println("getMBeanInfo\t\t" + + mbInfo); + System.out.println("---- OK\n") ; + + System.out.println("Check mxbean field in the MBeanInfo"); + String mxbeanField = + (String)mbInfo.getDescriptor().getFieldValue(JMX.MXBEAN_FIELD); + + if ( mxbeanField == null || ! mxbeanField.equals("true")) { + System.out.println("---- ERROR : Improper mxbean field value " + + mxbeanField); + errorCount++; + } + System.out.println("---- OK\n") ; + + System.out.println("Set attribute ObjectNameAtt"); + Attribute att = new Attribute("ObjectNameAtt", objName); + mbsc.setAttribute(objName, att); + ObjectName value = + (ObjectName)mbsc.getAttribute(objName, "ObjectNameAtt"); + + if ( ! value.equals(objName) ) { + errorCount++; + System.out.println("---- ERROR : setAttribute failed, got " + + value + + " while expecting " + + objName); + } + System.out.println("---- OK\n") ; + + System.out.println("Call operation doNothing"); + mbsc.invoke(objName, "doNothing", null, null); + System.out.println("---- OK\n") ; + + System.out.println("Call operation getWeather"); + Object weather = mbsc.invoke(objName, + "getWeather", + new Object[]{Boolean.TRUE}, + new String[]{"boolean"}); + System.out.println("Weather is " + weather); + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + private void printMBeanInfo(MBeanInfo mbInfo) { + System.out.println("Description " + mbInfo.getDescription()); + + for (MBeanConstructorInfo ctor : mbInfo.getConstructors()) { + System.out.println("Constructor " + ctor.getName()); + } + + for (MBeanAttributeInfo att : mbInfo.getAttributes()) { + System.out.println("Attribute " + att.getName() + + " [" + att.getType() + "]"); + } + + for (MBeanOperationInfo oper : mbInfo.getOperations()) { + System.out.println("Operation " + oper.getName()); + } + + for (MBeanNotificationInfo notif : mbInfo.getNotifications()) { + System.out.println("Notification " + notif.getName()); + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/MXBeanLoadingTest1.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/MXBeanLoadingTest1.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8058865 + * @summary Checks correct collection of MXBean's class after unregistration + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @run main/othervm/timeout=300 MXBeanLoadingTest1 + */ + +import java.lang.ref.WeakReference; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.Map; +import javax.management.Attribute; +import javax.management.JMX; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MXBean; +import javax.management.ObjectName; +import javax.management.loading.PrivateMLet; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; + +public class MXBeanLoadingTest1 { + + public static void main(String[] args) throws Exception { + MXBeanLoadingTest1 test = new MXBeanLoadingTest1(); + test.run((Map)null); + } + + + public void run(Map args) { + + System.out.println("MXBeanLoadingTest1::run: Start") ; + + try { + System.out.println("We ensure no reference is retained on MXBean class" + + " after it is unregistered. We take time to perform" + + " some little extra check of Descriptors, MBean*Info."); + + ClassLoader myClassLoader = MXBeanLoadingTest1.class.getClassLoader(); + + if (!(myClassLoader instanceof URLClassLoader)) { + String message = "(ERROR) Test's class loader is not " + + "a URLClassLoader"; + System.out.println(message); + throw new RuntimeException(message); + } + + URLClassLoader myURLClassLoader = (URLClassLoader) myClassLoader; + URL[] urls = myURLClassLoader.getURLs(); + PrivateMLet mlet = new PrivateMLet(urls, null, false); + Class shadowClass = mlet.loadClass(TestMXBean.class.getName()); + + if (shadowClass == TestMXBean.class) { + String message = "(ERROR) MLet got original TestMXBean, not shadow"; + System.out.println(message); + throw new RuntimeException(message); + } + shadowClass = null; + + MBeanServer mbs = MBeanServerFactory.createMBeanServer(); + ObjectName mletName = new ObjectName("x:type=mlet"); + mbs.registerMBean(mlet, mletName); + + ObjectName testName = new ObjectName("x:type=test"); + mbs.createMBean(Test.class.getName(), testName, mletName); + + // That test fails because the MXBean instance is accessed via + // a delegate OpenMBean which has + ClassLoader testLoader = mbs.getClassLoaderFor(testName); + + if (testLoader != mlet) { + System.out.println("MLet " + mlet); + String message = "(ERROR) MXBean's class loader is not MLet: " + + testLoader; + System.out.println(message); + throw new RuntimeException(message); + } + testLoader = null; + + + // Cycle get/set/get of the attribute of type Luis. + // We check the set is effective. + CompositeData cd_B = (CompositeData)mbs.getAttribute(testName, "B"); + CompositeType compType_B = cd_B.getCompositeType(); + + CompositeDataSupport cds_B = + new CompositeDataSupport(compType_B, + new String[]{"something"}, + new Object[]{Integer.valueOf(13)}); + Attribute myAtt = new Attribute("B", cds_B); + mbs.setAttribute(testName, myAtt); + + CompositeData cd_B2 = (CompositeData)mbs.getAttribute(testName, "B"); + + if ( ((Integer)cd_B2.get("something")).intValue() != 13 ) { + String message = "(ERROR) The setAttribute of att B did not work;" + + " expect Luis.something = 13 but got " + + cd_B2.get("something"); + System.out.println(message); + throw new RuntimeException(message); + } + + MBeanInfo info = mbs.getMBeanInfo(testName); + String mxbeanField = + (String)info.getDescriptor().getFieldValue(JMX.MXBEAN_FIELD); + + if ( mxbeanField == null || ! mxbeanField.equals("true")) { + String message = "(ERROR) Improper mxbean field value " + + mxbeanField; + System.out.println(message); + throw new RuntimeException(message); + } + + // Check the 2 attributes. + MBeanAttributeInfo[] attrs = info.getAttributes(); + + if ( attrs.length == 2 ) { + for (MBeanAttributeInfo mbai : attrs) { + String originalTypeFieldValue = + (String)mbai.getDescriptor().getFieldValue(JMX.ORIGINAL_TYPE_FIELD); + OpenType openTypeFieldValue = + (OpenType)mbai.getDescriptor().getFieldValue(JMX.OPEN_TYPE_FIELD); + + if ( mbai.getName().equals("A") ) { + if ( !mbai.isReadable() || !mbai.isWritable() + || mbai.isIs() + || !mbai.getType().equals("int") ) { + String message = "(ERROR) Unexpected MBeanAttributeInfo for A " + + mbai; + System.out.println(message); + throw new RuntimeException(message); + } + + if ( ! originalTypeFieldValue.equals("int") ) { + String message = "(ERROR) Unexpected originalType in Descriptor for A " + + originalTypeFieldValue; + System.out.println(message); + throw new RuntimeException(message); + } + + if ( ! openTypeFieldValue.equals(SimpleType.INTEGER) ) { + String message = "(ERROR) Unexpected openType in Descriptor for A " + + originalTypeFieldValue; + System.out.println(message); + throw new RuntimeException(message); + } + } else if ( mbai.getName().equals("B") ) { + if ( !mbai.isReadable() || !mbai.isWritable() + || mbai.isIs() + || !mbai.getType().equals("javax.management.openmbean.CompositeData") ) { + String message = "(ERROR) Unexpected MBeanAttributeInfo for B " + + mbai; + System.out.println(message); + throw new RuntimeException(message); + } + + if ( ! originalTypeFieldValue.equals(Luis.class.getName()) ) { + String message = "(ERROR) Unexpected originalType in Descriptor for B " + + originalTypeFieldValue; + System.out.println(message); + throw new RuntimeException(message); + } + + if ( ! openTypeFieldValue.equals(compType_B) ) { + String message = "(ERROR) Unexpected openType in Descriptor for B " + + compType_B; + System.out.println(message); + throw new RuntimeException(message); + } + } else { + String message = "(ERROR) Unknown attribute name"; + System.out.println(message); + throw new RuntimeException(message); + } + } + } else { + String message = "(ERROR) Unexpected MBeanAttributeInfo array" + + Arrays.deepToString(attrs); + System.out.println(message); + throw new RuntimeException(message); + } + + // Check the MXBean operation. + MBeanOperationInfo[] ops = info.getOperations(); + // The impact is ACTION_INFO as for a standard MBean it is UNKNOWN, + // logged 6320104. + if (ops.length != 1 || !ops[0].getName().equals("bogus") + || ops[0].getSignature().length > 0 + || !ops[0].getReturnType().equals("void")) { + String message = "(ERROR) Unexpected MBeanOperationInfo array " + + Arrays.deepToString(ops); + System.out.println(message); + throw new RuntimeException(message); + } + + String originalTypeFieldValue = + (String)ops[0].getDescriptor().getFieldValue(JMX.ORIGINAL_TYPE_FIELD); + OpenType openTypeFieldValue = + (OpenType)ops[0].getDescriptor().getFieldValue(JMX.OPEN_TYPE_FIELD); + + if ( ! originalTypeFieldValue.equals("void") ) { + String message = "(ERROR) Unexpected originalType in Descriptor for bogus " + + originalTypeFieldValue; + System.out.println(message); + throw new RuntimeException(message); + } + + if ( ! openTypeFieldValue.equals(SimpleType.VOID) ) { + String message = "(ERROR) Unexpected openType in Descriptor for bogus " + + originalTypeFieldValue; + System.out.println(message); + throw new RuntimeException(message); + } + + // Check there is 2 constructors. + if (info.getConstructors().length != 2) { + String message = "(ERROR) Wrong number of constructors " + + "in introspected bean: " + + Arrays.asList(info.getConstructors()); + System.out.println(message); + throw new RuntimeException(message); + } + + // Check MXBean class name. + if (!info.getClassName().endsWith("Test")) { + String message = "(ERROR) Wrong info class name: " + + info.getClassName(); + System.out.println(message); + throw new RuntimeException(message); + } + + mbs.unregisterMBean(testName); + mbs.unregisterMBean(mletName); + + WeakReference mletRef = + new WeakReference(mlet); + mlet = null; + + System.out.println("MXBean registered and unregistered, waiting for " + + "garbage collector to collect class loader"); + + for (int i = 0; i < 10000 && mletRef.get() != null; i++) { + System.gc(); + Thread.sleep(1); + } + + if (mletRef.get() == null) + System.out.println("(OK) class loader was GC'd"); + else { + String message = "(ERROR) Class loader was not GC'd"; + System.out.println(message); + throw new RuntimeException(message); + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + throw new RuntimeException(e); + } + + System.out.println("MXBeanLoadingTest1::run: Done without any error") ; + } + + + // I agree the use of the MXBean annotation and the MXBean suffix for the + // interface name are redundant but however harmless. + // + @MXBean(true) + public static interface TestMXBean { + public void bogus(); + public int getA(); + public void setA(int a); + public Luis getB(); + public void setB(Luis mi); + } + + + public static class Test implements TestMXBean { + private Luis luis = new Luis() ; + public Test() {} + public Test(int x) {} + + public void bogus() {} + public int getA() {return 0;} + public void setA(int a) {} + public Luis getB() {return this.luis;} + public void setB(Luis luis) {this.luis = luis;} + } + + + public static class Luis { + private int something = 0; + public Luis() {} + public int getSomething() {return something;} + public void setSomething(int v) {something = v;} + public void doNothing() {} + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/MXBeanNotifTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/MXBeanNotifTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8058865 + * @summary Checks MXBean proper registration both as its implementation class and interface + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile Basic.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanNotifTest -numOfNotifications 239 -timeForNotificationInSeconds 4 + */ + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +import java.lang.management.ManagementFactory; + +import javax.management.Attribute; +import javax.management.Descriptor; +import javax.management.ImmutableDescriptor; +import javax.management.MBeanServer; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; + +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.openmbean.CompositeType; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +public class MXBeanNotifTest implements NotificationListener { + + private static String BASIC_MXBEAN_CLASS_NAME = "Basic"; + private static String BASIC_MXBEAN_INTERFACE_NAME = "BasicMXBean"; + + private long timeForNotificationInSeconds = 3L; + private int numOfNotifications = 1; + private BlockingQueue notifList = null; + private int numOfNotifDescriptorElements = 13; + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + MXBeanNotifTest test = new MXBeanNotifTest(); + test.run(map); + + } + + protected void parseArgs(Map args) throws Exception { + + String arg = null; + + // Init numOfNotifications + // It is the number of notifications we should trigger and check. + arg = (String)args.get("-numOfNotifications") ; + if (arg != null) { + numOfNotifications = (new Integer(arg)).intValue(); + } + + // Init timeForNotificationInSeconds + // It is the maximum time in seconds we wait for each notification. + arg = (String)args.get("-timeForEachNotificationInSeconds") ; + if (arg != null) { + timeForNotificationInSeconds = (new Long(arg)).longValue(); + } + + } + + public void run(Map args) { + + System.out.println("MXBeanNotifTest::run: Start") ; + int errorCount = 0 ; + + try { + parseArgs(args); + notifList = new ArrayBlockingQueue(numOfNotifications); + + // JMX MbeanServer used inside single VM as if remote. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // ---- + System.out.println("MXBeanNotifTest::run: Create and register the MBean"); + ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ; + mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName); + System.out.println("---- OK\n") ; + + // ---- + System.out.println("MXBeanNotifTest::run: Add me as notification listener"); + mbsc.addNotificationListener(objName, this, null, null); + + // ---- + System.out.println("MXBeanNotifTest::run: Retrieve the Descriptor" + + " that should be in MBeanNotificationInfo"); + TabularData tabData = + (TabularData)mbsc.getAttribute(objName, "NotifDescriptorAsMapAtt"); + Map descrMap = new HashMap<>(); + + for (Iterator it = tabData.values().iterator(); it.hasNext(); ) { + CompositeData compData = (CompositeData)it.next(); + descrMap.put((String)compData.get("key"), + (String)compData.get("value")); + } + + Descriptor refNotifDescriptor = new ImmutableDescriptor(descrMap); + System.out.println("---- OK\n") ; + + // ---- + // Because the MBean holding the targeted attribute is MXBean, we + // should use for the setAttribute a converted form for the + // attribute value as described by the MXBean mapping rules. + // This explains all that lovely stuff for creating a + // TabularDataSupport. + // + // WARNING : the MBeanInfo of the MXBean used on opposite side + // is computed when the MBean is registered. + // It means the Descriptor considered for the MBeanNotificationInfo + // is not the one we set in the lines below, it is too late. + // However, we check that set is harmless when we check + // the MBeanNotificationInfo. + // + System.out.println("MXBeanNotifTest::run: Set a Map" + + " attribute"); + String typeName = + "java.util.Map"; + String[] keyValue = new String[] {"key", "value"}; + OpenType[] openTypes = + new OpenType[] {SimpleType.STRING, SimpleType.STRING}; + CompositeType rowType = new CompositeType(typeName, typeName, + keyValue, keyValue, openTypes); + TabularType tabType = new TabularType(typeName, typeName, + rowType, new String[]{"key"}); + TabularDataSupport convertedDescrMap = + new TabularDataSupport(tabType); + + for (int i = 0; i < numOfNotifDescriptorElements; i++) { + Object[] descrValue = {"field" + i, "value" + i}; + CompositeData data = + new CompositeDataSupport(rowType, keyValue, descrValue); + convertedDescrMap.put(data); + } + + Attribute descrAtt = + new Attribute("NotifDescriptorAsMapAtt", convertedDescrMap); + mbsc.setAttribute(objName, descrAtt); + System.out.println("---- OK\n") ; + + // ---- + System.out.println("MXBeanNotifTest::run: Compare the Descriptor from" + + " the MBeanNotificationInfo against a reference"); + MBeanInfo mbInfo = mbsc.getMBeanInfo(objName); + errorCount += checkMBeanInfo(mbInfo, refNotifDescriptor); + System.out.println("---- DONE\n") ; + + // ---- + System.out.println("Check isInstanceOf(Basic)"); + + if ( ! mbsc.isInstanceOf(objName, BASIC_MXBEAN_CLASS_NAME) ) { + errorCount++; + System.out.println("---- ERROR isInstanceOf returned false\n") ; + } else { + System.out.println("---- OK\n") ; + } + + // ---- + System.out.println("Check isInstanceOf(BasicMXBean)"); + + if ( ! mbsc.isInstanceOf(objName, BASIC_MXBEAN_INTERFACE_NAME) ) { + errorCount++; + System.out.println("---- ERROR isInstanceOf returned false\n") ; + } else { + System.out.println("---- OK\n") ; + } + + // ---- + System.out.println("MXBeanNotifTest::run: Ask for " + + numOfNotifications + " notification(s)"); + Object[] sendNotifParam = new Object[1]; + String[] sendNotifSig = new String[]{"java.lang.String"}; + + for (int i = 0; i < numOfNotifications; i++) { + // Select which type of notification we ask for + if ( i % 2 == 0 ) { + sendNotifParam[0] = Basic.NOTIF_TYPE_0; + } else { + sendNotifParam[0] = Basic.NOTIF_TYPE_1; + } + + // Trigger notification emission + mbsc.invoke(objName, + "sendNotification", + sendNotifParam, + sendNotifSig); + + // Wait for it then check it when it comes early enough + Notification notif = + notifList.poll(timeForNotificationInSeconds, + TimeUnit.SECONDS) ; + // The very first notification is likely to come in slower than + // all the others. Because that test isn't targeting the speed + // notifications are delivered with, we prefer to secure it. + if (i == 0 && notif == null) { + System.out.println("MXBeanNotifTest::run: Wait extra " + + timeForNotificationInSeconds + " second(s) the " + + " very first notification"); + notif = notifList.poll(timeForNotificationInSeconds, + TimeUnit.SECONDS); + } + + if ( notif == null ) { + errorCount++; + System.out.println("---- ERROR No notification received" + + " within allocated " + timeForNotificationInSeconds + + " second(s) !"); + } else { + errorCount += + checkNotification(notif, + (String)sendNotifParam[0], + Basic.NOTIFICATION_MESSAGE, + objName); + } + } + + int toc = 0; + while ( notifList.size() < 2 && toc < 10 ) { + Thread.sleep(499); + toc++; + } + System.out.println("---- DONE\n") ; + } catch(Exception e) { + Utils.printThrowable(e, true) ; + throw new RuntimeException(e); + } + + if ( errorCount == 0 ) { + System.out.println("MXBeanNotifTest::run: Done without any error") ; + } else { + System.out.println("MXBeanNotifTest::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + } + + + private int checkMBeanInfo(MBeanInfo mbi, Descriptor refDescr) { + MBeanNotificationInfo[] notifsInfo = mbi.getNotifications(); + int res = 0; + + for (MBeanNotificationInfo mbni : notifsInfo) { + if ( mbni.getDescriptor().equals(refDescr) ) { + System.out.println("(OK)"); + } else { + System.out.println("(ERROR) Descriptor of the notification is " + + mbni.getDescriptor() + + " as we expect " + + refDescr); + res++; + } + } + + return res; + } + + + private int checkNotification(Notification notif, + String refType, + String refMessage, + ObjectName refSource) { + int res = 0; + + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getSource " + notif.getSource()); + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getMessage " + notif.getMessage()); + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getSequenceNumber " + notif.getSequenceNumber()); + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getTimeStamp " + notif.getTimeStamp()); + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getType " + notif.getType()); + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getUserData " + notif.getUserData()); + + if ( ! notif.getType().equals(refType) ) { + res++; + System.out.println("(ERROR) Type is not " + + refType + " in notification\n" + notif); + } else { + if ( notif.getType().equals(Basic.NOTIF_TYPE_0) + && ! (notif instanceof javax.management.Notification) ) { + res++; + System.out.println("(ERROR) Notification is not instance of " + + " javax.management.Notification but rather " + + notif.getClass().getName()); + } else if ( notif.getType().equals(Basic.NOTIF_TYPE_1) + && ! (notif instanceof SqeNotification) ) { + res++; + System.out.println("(ERROR) Notification is not instance of " + + " javasoft.sqe.jmx.share.SqeNotification but rather " + + notif.getClass().getName()); + } + } + + if ( ! notif.getMessage().equals(refMessage) ) { + res++; + System.out.println("(ERROR) Message is not " + + refMessage + " in notification\n" + notif); + } + + if ( ! notif.getSource().equals(refSource) ) { + res++; + System.out.println("(ERROR) Source is not " + + refSource + " in notification\n" + notif); + } + + return res; + } + + public void handleNotification(Notification notification, Object handback) { + Utils.debug(Utils.DEBUG_VERBOSE, + "MXBeanNotifTest::handleNotification: Received " + + notification); + notifList.add(notification); + } + +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/MXBeanWeirdParamTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/MXBeanWeirdParamTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8058865 + * @summary Checks that a serialized instance is not transmitted from an MXBean. + * All the communication should be done via Open Types + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile Basic.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanWeirdParamTest + */ + +import java.util.Map; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +import java.lang.Process; +import java.lang.management.ManagementFactory; + +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.ObjectName; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JDKToolFinder; + +public class MXBeanWeirdParamTest { + + private static String BASIC_MXBEAN_CLASS_NAME = "Basic"; + + private static final String CLIENT_CLASS_MAIN = + "MXBeanWeirdParamTest$ClientSide"; + + private JMXConnectorServer cs; + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + MXBeanWeirdParamTest test = new MXBeanWeirdParamTest(); + test.run(map); + + } + + /* + * Create the MBeansServe side of the test and returns its address + */ + private JMXServiceURL createServerSide() throws Exception { + final int NINETY_SECONDS = 90; + + // We will use the platform mbean server + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + Utils.waitReady(cs, NINETY_SECONDS); + + JMXServiceURL addr = cs.getAddress(); + return addr; + } + + + /* + * Creating command-line for running subprocess JVM: + * + * JVM command line is like: + * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main + * + * {defaultopts} are the default java options set by the framework. + * + */ + private List buildCommandLine() { + List opts = new ArrayList<>(); + opts.add(JDKToolFinder.getJDKTool("java")); + opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts())); + // We need to set WEIRD_PARAM propertty on the client-side + opts.add("-DWEIRD_PARAM"); + opts.add("-cp"); + opts.add(System.getProperty("test.class.path", "test.class.path")); + opts.add(CLIENT_CLASS_MAIN); + + return opts; + } + + /** + * Runs MXBeanWeirdParamTest$ClientSide with the passed options and redirects + * subprocess standard I/O to the current (parent) process. This provides a + * trace of what happens in the subprocess while it is runnning (and before + * it terminates). + * + * @param serviceUrlStr string representing the JMX service Url to connect to. + */ + private int runClientSide(String serviceUrlStr) throws Exception { + + // Building command-line + List opts = buildCommandLine(); + opts.add(serviceUrlStr); + + // Launch separate JVM subprocess + int exitCode = 0; + String[] optsArray = opts.toArray(new String[0]); + ProcessBuilder pb = new ProcessBuilder(optsArray); + Process p = ProcessTools.startProcess("MXBeanWeirdParamTest$ClientSide", pb); + + // Handling end of subprocess + try { + exitCode = p.waitFor(); + if (exitCode != 0) { + System.out.println( + "Subprocess unexpected exit value of [" + exitCode + + "]. Expected 0.\n"); + } + } catch (InterruptedException e) { + System.out.println("Parent process interrupted with exception : \n " + e + " :" ); + + // Parent thread unknown state, killing subprocess. + p.destroyForcibly(); + + throw new RuntimeException( + "Parent process interrupted with exception : \n " + e + " :" ); + } finally { + return exitCode; + } + + } + + public void run(Map args) throws Exception { + + System.out.println("MXBeanWeirdParamTest::run: Start") ; + int errorCount = 0; + + try { + // Initialise the server side + JMXServiceURL urlToUse = createServerSide(); + + // Run client side + errorCount = runClientSide(urlToUse.toString()); + + if ( errorCount == 0 ) { + System.out.println("MXBeanWeirdParamTest::run: Done without any error") ; + } else { + System.out.println("MXBeanWeirdParamTest::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + + cs.stop(); + + } catch(Exception e) { + throw new RuntimeException(e); + } + + } + + private static class ClientSide { + public static void main(String args[]) throws Exception { + + int errorCount = 0 ; + String msgTag = "ClientSide::main: "; + + try { + + // Get a connection to remote mbean server + JMXServiceURL addr = new JMXServiceURL(args[0]); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // ---- + System.out.println(msgTag + "Create and register the MBean"); + ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ; + mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName); + System.out.println(msgTag +"---- OK\n") ; + + // ---- + System.out.println(msgTag +"Get attribute SqeParameterAtt on our MXBean"); + Object result = mbsc.getAttribute(objName, "SqeParameterAtt"); + System.out.println(msgTag +"(OK) Got result of class " + + result.getClass().getName()); + System.out.println(msgTag +"Received CompositeData is " + result); + System.out.println(msgTag +"---- OK\n") ; + + // ---- + // We use the value returned by getAttribute to perform the invoke. + System.out.println(msgTag +"Call operation doWeird on our MXBean [1]"); + mbsc.invoke(objName, "doWeird", + new Object[]{result}, + new String[]{"javax.management.openmbean.CompositeData"}); + System.out.println(msgTag +"---- OK\n") ; + + // ---- + // We build the CompositeData ourselves that time. + System.out.println(msgTag +"Call operation doWeird on our MXBean [2]"); + String typeName = "SqeParameter"; + String[] itemNames = new String[] {"glop"}; + OpenType[] openTypes = new OpenType[] {SimpleType.STRING}; + CompositeType rowType = new CompositeType(typeName, typeName, + itemNames, itemNames, openTypes); + Object[] itemValues = {"HECTOR"}; + CompositeData data = + new CompositeDataSupport(rowType, itemNames, itemValues); + TabularType tabType = new TabularType(typeName, typeName, + rowType, new String[]{"glop"}); + TabularDataSupport tds = new TabularDataSupport(tabType); + tds.put(data); + System.out.println(msgTag +"Source CompositeData is " + data); + mbsc.invoke(objName, "doWeird", + new Object[]{data}, + new String[]{"javax.management.openmbean.CompositeData"}); + System.out.println(msgTag +"---- OK\n") ; + + // ---- + System.out.println(msgTag +"Unregister the MBean"); + mbsc.unregisterMBean(objName); + System.out.println(msgTag +"---- OK\n") ; + + // Terminate the JMX Client + cc.close(); + + } catch(Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + throw new RuntimeException(e); + } finally { + System.exit(errorCount); + } + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/SqeDescriptorKey.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/SqeDescriptorKey.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import javax.management.DescriptorKey; + +/** + * That annotation is usable everywhere DescriptorKey is (and even more). + * It is for use to test that you can retrieve the SqeDescriptorKey into the + * appropriate Descriptor instances as built by the JMX runtime. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface SqeDescriptorKey { + @DescriptorKey("sqeDescriptorKey") + String value(); + + // List descriptor fields that may be added or may be updated + // when retrieving an MBeanInfo using a JMXWS connection compared to the + // MBeanInfo returned by a local MBeanServer. + // The annotation format is : + // = + // The values actually handled by the test suite are : + // openType=SimpleType.VOID + @DescriptorKey("descriptorFields") + String[] descriptorFields() default {}; +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/SqeNotification.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/SqeNotification.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 javax.management.Notification; + +/** + * Could hold someday a specific semantic. + * For now it is used to have a Notification which of another class, no more. + */ +public class SqeNotification extends Notification { + + /** Creates a new instance of SqeNotification */ + public SqeNotification(String type, Object source, long sequenceNumber) { + super(type, source, sequenceNumber); + } + + /** Creates a new instance of SqeNotification */ + public SqeNotification(String type, Object source, long sequenceNumber, + long timeStamp) { + super(type, source, sequenceNumber, timeStamp); + } + + /** Creates a new instance of SqeNotification */ + public SqeNotification(String type, Object source, long sequenceNumber, + long timeStamp, String message) { + super(type, source, sequenceNumber, timeStamp, message); + } + + /** Creates a new instance of SqeNotification */ + public SqeNotification(String type, Object source, long sequenceNumber, + String message) { + super(type, source, sequenceNumber, message); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/SqeParameter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/SqeParameter.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Serializable; + +/** + * That class is to use as an MBean operation parameter or returned value. + * The property Glop with its public getter + setter is only there to be + * reconstructible following MXBean specification, so that SqeParameter can be + * used for what it is designed to. + */ +public class SqeParameter implements Serializable { + + private static boolean weird; + private String glop; + + static { + if ( System.getProperty("WEIRD_PARAM") != null ) { + weird = true; + } + } + + /** + * Creates a new instance of SqeParameter. + *
    When the Java property WEIRD_PARAM is set, that constructor + * throws an exception. + *
    That can be used to ensure the class is instantiated on server side + * but never on client side. + */ + public SqeParameter() throws Exception { + if ( weird ) { + throw new Exception(); + } + } + + public String getGlop() { + return glop; + } + + public void setGlop(String value) { + glop = value; + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/mxbean/Utils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/mxbean/Utils.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Map; +import java.util.HashMap; +import java.util.Properties; +import java.lang.reflect.Method; +import javax.management.remote.JMXConnectorServerMBean; + +// utility class for MXBean* tests coming from JMX Tonga test suite +class Utils { + + // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property + private static final String DEBUG_HEADER = "[debug] "; + + // DEBUG levels + private static int selectedDebugLevel = 0; + static final int DEBUG_STANDARD = 1; + static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests + static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE; + + static void parseDebugProperties() { + int level = 0; + Properties p = System.getProperties(); + + // get selected levels + if (p.getProperty("DEBUG_STANDARD") != null) { + level |= DEBUG_STANDARD; + } + + if (p.getProperty("DEBUG_VERBOSE") != null) { + level |= DEBUG_VERBOSE; + } + + if (p.getProperty("DEBUG_ALL") != null) { + level |= DEBUG_ALL; + } + + selectedDebugLevel = level; + } + + /** + * Reproduces the original parsing and collection of test parameters + * from the DTonga JMX test suite. + * + * Collects passed args and returns them in a map(argname, value) structure, + * which will be then propagated as necessary to various called methods. + */ + static Map parseParameters(String args[]) + throws Exception { + Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start"); + HashMap map = new HashMap<>(); + + for ( int i = 0; i < args.length; i++ ) { + if ( args[i].trim().startsWith("-") ) { + if ((i+1) < args.length && !args[i+1].startsWith("-") ) { + Utils.debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with value " + + args[i+1]) ; + map.put(args[i].trim(), args[i+1].trim()) ; + } else if ((i+1) < args.length && args[i+1].startsWith("-") || + (i+1) == args.length ) { + Utils.debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with null value") ; + map.put(args[i].trim(), null) ; + } else { + System.out.println( + "TestRoot::parseParameters: (WARNING) not added in map = " + + args[i]) ; + } + } + } + + Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ; + return map ; + } + + /** + * This method is to be used in all tests to print anything + * that is temporary. + * Printing is done only when debug is activated by the property DEBUG. + * Printing depends also on the DEBUG_LEVEL property. + * Here it encapsulates a System.out.println. + */ + public static void debug(int level, String line) { + if ((selectedDebugLevel & level) != 0) { + System.out.println(DEBUG_HEADER + line); + } + } + + /** + * Do print stack trace when withStack is true. + * Does try to call getTargetException() and getTargetError() then + * print embedded stacks in the case of an Exception wrapping + * another Exception or an Error. Recurse until no more wrapping + * is found. + */ + public static void printThrowable(Throwable theThro, boolean withStack) { + try { + if (withStack) { + theThro.printStackTrace(System.out); + } + if (theThro instanceof Exception) { + Exception t = (Exception) theThro; + Method target = null; + String blank = " "; + try { + target = t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not + } + System.out.println(blank + t.getClass() + "==>" + t.getMessage()); + while (target != null) { + try { + t = (Exception) target.invoke(t, + (java.lang.Object[]) null); + } catch (Exception ee) { + t = null; + } + try { + if (t != null) { + blank = blank + " "; + System.out.println(blank + t.getClass() + "==>" + + t.getMessage()); + try { + target = + t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not } + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + + // We may have exceptions wrapping an Error then it is + // getTargetError that is likely to be called + try { + target = ((Exception) theThro).getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + Throwable err = theThro; + while (target != null) { + try { + err = (Error) target.invoke(err, + (java.lang.Object[]) null); + } catch (Exception ee) { + err = null; + } + try { + if (err != null) { + blank = blank + " "; + System.out.println(blank + err.getClass() + "==>" + + err.getMessage()); + if (withStack) { + err.printStackTrace(System.out); + } + try { + target = err.getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + } else { + System.out.println("Throwable is : " + theThro); + } + } catch (Throwable x) { + System.out.println("Exception : raised in printException : " + x); + } + } + + /** + * Wait up to maxTimeInSeconds second(s) the given JMX connector server + * comes up (which means isActive returns true). + * If it fails to do so we throw a RunTime exception. + */ + public static void waitReady(JMXConnectorServerMBean server, + int maxTimeInSeconds) throws Exception { + int elapsed = 0; + + while (!server.isActive() && elapsed < maxTimeInSeconds) { + Thread.sleep(1000); + elapsed++; + } + + if (server.isActive()) { + String message = "Utils::waitReady: JMX connector server came up"; + if ( elapsed == 0) { + message += " immediately"; + } else { + message += " after " + elapsed + " seconds"; + } + message += " [" + server.getAddress() + "]"; + Utils.debug(DEBUG_STANDARD, message); + } else { + String message = "Utils::waitReady: (ERROR) JMX connector" + + " server didn't come up after " + elapsed + " seconds [" + + server.getAddress() + "]"; + System.out.println(message); + throw new RuntimeException(message); + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/query/QueryData.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/query/QueryData.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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. + */ + +public abstract class QueryData { + protected int intValue = 9; + protected long longValue = 9L; + protected Integer integerValue = Integer.valueOf(9); + protected boolean booleanValue = true; + protected double doubleValue = 9D; + protected float floatValue = 9.0F; + protected String stringValue = "9"; +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/query/QueryFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/query/QueryFactory.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.ArrayList; + +import javax.management.Query; +import javax.management.QueryExp; +import javax.management.ValueExp; + +/** + * Class used for building QueryExp instances of all every possible type + * in terms of JMX API members; note that several JMX classes are private + * and appears in the JDK API only by their serial form. + * Comments in each case of the big switch in method getQuery() details which + * API member we cover with a given query. + */ +public class QueryFactory extends QueryData { + + private String mbeanClassName = ""; + private String primitiveIntAttName = "IntAtt"; + private String primitiveLongAttName = "LongAtt"; + private String integerAttName = "IntegerAtt"; + private String primitiveBooleanAttName = "BooleanAtt"; + private String primitiveDoubleAttName = "DoubleAtt"; + private String primitiveFloatAttName = "FloatAtt"; + private String stringAttName = "StringAtt"; + private ArrayList queries = new ArrayList(); + + /** + * Creates a new instance of QueryFactory. + * The name is the fully qualified class name of an MBean. + * There is severe constraints on that MBean that must: + *
      + *
    • extend QueryData in order to inherit attribute values. + *
    • define a RW attribute IntAtt of type int + * initialized to QueryData.longValue + *
    • define a RW attribute LongAtt of type long + * initialized to QueryData.intValue + *
    • define a RW attribute IntegerAtt of type Integer + * initialized to QueryData.integerValue + *
    • define a RW attribute BooleanAtt of type boolean + * initialized to QueryData.booleanValue + *
    • define a RW attribute DoubleAtt of type double + * initialized to QueryData.doubleValue + *
    • define a RW attribute FloatAtt of type float + * initialized to QueryData.floatValue + *
    • define a RW attribute StringAtt of type String + * initialized to QueryData.stringValue + *
    + */ + public QueryFactory(String name) { + this.mbeanClassName = name; + } + + /** + * Returns the highest index value the method getQuery supports. + * WARNING : returns 0 if buildQueries haven't been called first ! + */ + public int getSize() { + return queries.size(); + } + + /** + * Populates an ArrayList of QueryExp. + * Lowest index is 1. + * Highest index is returned by getSize(). + *
    The queries numbered 1 to 23 allow to cover all the underlying + * Java classes of the JMX API used to build queries. + */ + public void buildQueries() { + if ( queries.size() == 0 ) { + int smallerIntValue = intValue - 1; + int biggerIntValue = intValue + 1; + + // case 1: + // True if the MBean is of class mbeanClassName + // We cover javax.management.InstanceOfQueryExp + queries.add(Query.isInstanceOf(Query.value(mbeanClassName))); + + // case 2: + // True if the MBean is of class mbeanClassName + // We cover javax.management.MatchQueryExp and + // javax.management.ClassAttributeValueExp + queries.add(Query.match(Query.classattr(), + Query.value(mbeanClassName))); + + // case 3: + // True if an attribute named primitiveIntAttName of type int has + // the value intValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.NumericValueExp + queries.add(Query.eq(Query.attr(primitiveIntAttName), + Query.value(intValue))); + + // case 4: + // True if an attribute named primitiveLongAttName of type long has + // the value longValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.NumericValueExp + queries.add(Query.eq(Query.attr(primitiveLongAttName), + Query.value(longValue))); + + // case 5: + // True if an attribute named primitiveDoubleAttName of type double + // has the value doubleValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.NumericValueExp + queries.add(Query.eq(Query.attr(primitiveDoubleAttName), + Query.value(doubleValue))); + + // case 6: + // True if an attribute named primitiveFloatAttName of type float + // has the value floatValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.NumericValueExp + queries.add(Query.eq(Query.attr(primitiveFloatAttName), + Query.value(floatValue))); + + // case 7: + // True if an attribute named primitiveIntAttName of type int is + // hold by an MBean of class mbeanClassName and has + // the value intValue + // We cover javax.management.QualifiedAttributeValueExp + queries.add(Query.eq(Query.attr(mbeanClassName, primitiveIntAttName), + Query.value(intValue))); + + // case 8: + // True if an attribute named stringAttName of type String has + // the value stringValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.StringValueExp + queries.add(Query.eq(Query.attr(stringAttName), + Query.value(stringValue))); + + // case 9: + // True if an attribute named integerAttName of type Integer has + // the value integerValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.NumericValueExp + queries.add(Query.eq(Query.attr(integerAttName), + Query.value(integerValue))); + + // case 10: + // True if an attribute named primitiveBooleanAttName of type boolean + // has the value booleanValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.BooleanValueExp + queries.add(Query.eq(Query.attr(primitiveBooleanAttName), + Query.value(booleanValue))); + + // case 11: + // True if an attribute named primitiveIntAttName of type int has + // not the value smallerIntValue + // We cover javax.management.NotQueryExp + queries.add(Query.not(Query.eq(Query.attr(primitiveIntAttName), + Query.value(smallerIntValue)))); + + // case 12: + // True if either + // an attribute named primitiveIntAttName of type int has + // the value intValue + // or + // an attribute named primitiveLongAttName of type long has + // the value longValue + // We cover javax.management.OrQueryExp + queries.add(Query.or( + Query.eq(Query.attr(primitiveIntAttName), + Query.value(intValue)), + Query.eq(Query.attr(primitiveLongAttName), + Query.value(longValue)))); + + // case 13: + // True if + // an attribute named primitiveIntAttName of type int has + // the value intValue + // and + // an attribute named primitiveLongAttName of type long has + // the value longValue + // We cover javax.management.AndQueryExp + queries.add(Query.and( + Query.eq(Query.attr(primitiveIntAttName), + Query.value(intValue)), + Query.eq(Query.attr(primitiveLongAttName), + Query.value(longValue)))); + + // case 14: + // True if an attribute named primitiveIntAttName of type int has + // the value intValue + // We cover javax.management.InQueryExp + ValueExp[] inArray = {Query.value(intValue)}; + queries.add(Query.in(Query.attr(primitiveIntAttName), inArray)); + + // case 15: + // True if an attribute named primitiveIntAttName of type int has + // its value in between smallerIntValue and biggerIntValue + // We cover javax.management.BetweenRelQueryExp + queries.add(Query.between(Query.attr(primitiveIntAttName), + Query.value(smallerIntValue), + Query.value(biggerIntValue))); + + // case 16: + // True if an attribute named primitiveIntAttName of type int has + // a value greater than smallerIntValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to GT + queries.add(Query.gt(Query.attr(primitiveIntAttName), + Query.value(smallerIntValue))); + + // case 17: + // True if an attribute named primitiveIntAttName of type int has + // a value greater or equal to smallerIntValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to GE + queries.add(Query.geq(Query.attr(primitiveIntAttName), + Query.value(smallerIntValue))); + + // case 18: + // True if an attribute named primitiveIntAttName of type int has + // a value smaller than biggerIntValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to LT + queries.add(Query.lt(Query.attr(primitiveIntAttName), + Query.value(biggerIntValue))); + + // case 19: + // True if an attribute named primitiveIntAttName of type int has + // a value smaller or equal to biggerIntValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to LE + queries.add(Query.leq(Query.attr(primitiveIntAttName), + Query.value(biggerIntValue))); + + // case 20: + // True if an attribute named primitiveIntAttName of type int has + // a value equal to intValue minus zero + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to MINUS + queries.add(Query.eq(Query.attr(primitiveIntAttName), + Query.minus(Query.value(intValue), Query.value(0)))); + + // case 21: + // True if an attribute named primitiveIntAttName of type int has + // a value equal to intValue plus zero + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to PLUS + queries.add(Query.eq(Query.attr(primitiveIntAttName), + Query.plus(Query.value(intValue), Query.value(0)))); + + // case 22: + // True if an attribute named primitiveIntAttName of type int has + // a value equal to intValue divided by one + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to DIV + queries.add(Query.eq(Query.attr(primitiveIntAttName), + Query.div(Query.value(intValue), Query.value(1)))); + + // case 23: + // True if an attribute named primitiveIntAttName of type int has + // a value equal to intValue multiplicated by one + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to TIMES + queries.add(Query.eq(Query.attr(primitiveIntAttName), + Query.times(Query.value(intValue), Query.value(1)))); + + // case 24: + // That query is a complex one that combines within a big AND + // queries with index 2 to 23 inclusive. But because a List is + // zero based, we must decrement all indexes by 1 when retrieving + // any previously stored query. + QueryExp q2_3 = Query.and(queries.get(2-1), queries.get(3-1)); + QueryExp q4_5 = Query.and(queries.get(4-1), queries.get(5-1)); + QueryExp q6_7 = Query.and(queries.get(6-1), queries.get(7-1)); + QueryExp q8_9 = Query.and(queries.get(8-1), queries.get(9-1)); + QueryExp q10_11 = Query.and(queries.get(10-1), queries.get(11-1)); + QueryExp q12_13 = Query.and(queries.get(12-1), queries.get(13-1)); + QueryExp q14_15 = Query.and(queries.get(14-1), queries.get(15-1)); + QueryExp q16_17 = Query.and(queries.get(16-1), queries.get(17-1)); + QueryExp q18_19 = Query.and(queries.get(18-1), queries.get(19-1)); + QueryExp q20_21 = Query.and(queries.get(20-1), queries.get(21-1)); + QueryExp q22_23 = Query.and(queries.get(22-1), queries.get(23-1)); + QueryExp q2_5 = Query.and(q2_3, q4_5); + QueryExp q6_9 = Query.and(q6_7, q8_9); + QueryExp q10_13 = Query.and(q10_11, q12_13); + QueryExp q14_17 = Query.and(q14_15, q16_17); + QueryExp q18_21 = Query.and(q18_19, q20_21); + QueryExp q2_9 = Query.and(q2_5, q6_9); + QueryExp q10_17 = Query.and(q10_13, q14_17); + QueryExp q18_23 = Query.and(q18_21, q22_23); + QueryExp q2_17 = Query.and(q2_9, q10_17); + queries.add(Query.and(q2_17, q18_23)); + + // case 25: + // Complex query mixing AND and OR. + queries.add(Query.or(q6_9, q18_23)); + } + } + + /** + * Returns a QueryExp taken is the ArrayList populated by buildQueries(). + * Lowest index is 1. + * Highest index is returned by getSize(). + *
    The queries numbered 1 to 23 allow to cover all the underlying + * Java classes of the JMX API used to build queries. + */ + public QueryExp getQuery(int index) { + return queries.get(index - 1); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/query/ServerDelegate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/query/ServerDelegate.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import javax.management.remote.JMXServiceURL ; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.StandardMBean; + +/** + * This class defines an MBean that can be registered and used on client side + * to handle informations or properties of the remote server. + * + * For example, this MBean can store IOR addresses + * of RMI/IIOP connector(s) used in a test. + * + * That MBean might not be used for testing purpose itself. + */ +public class ServerDelegate implements ServerDelegateMBean, MBeanRegistration { + + private MBeanServer mbeanServer = null; + private List addresses = null; + private String port; + private static String javaVersion = System.getProperty("java.version"); + private int sqeJmxwsCredentialsProviderCallCount = 0; + private String jmxwsCredentialsProviderUrl = null; + private int testJMXAuthenticatorCallCount = 0; + private Principal testJMXAuthenticatorPrincipal = null; + + @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR ServerDelegate") + public ServerDelegate() { + addresses = new ArrayList(); + } + + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + // Initialize MBeanServer attribute + mbeanServer = server; + return name; + } + public void postRegister(Boolean registrationDone) { + } + public void preDeregister() throws Exception { + } + public void postDeregister() { + } + + public void addAddress(JMXServiceURL url) { + addresses.add(url) ; + } + + public List getAddresses() { + return addresses ; + } + + public void setPort(String p) { + port = p ; + } + + public String getPort() { + return port ; + } + + public String getJavaVersion() { + return javaVersion; + } + + public void sqeJmxwsCredentialsProviderCalled() { + sqeJmxwsCredentialsProviderCallCount++; + } + + public int getSqeJmxwsCredentialsProviderCallCount() { + return sqeJmxwsCredentialsProviderCallCount; + } + + public void setJmxwsCredentialsProviderUrl(String url) { + jmxwsCredentialsProviderUrl = url; + } + + public String getJmxwsCredentialsProviderUrl() { + return jmxwsCredentialsProviderUrl; + } + + public void testJMXAuthenticatorCalled() { + testJMXAuthenticatorCallCount++; + } + + public int getTestJMXAuthenticatorCallCount() { + return testJMXAuthenticatorCallCount; + } + + public void setTestJMXAuthenticatorPrincipal(Principal principal) { + testJMXAuthenticatorPrincipal = principal; + } + + public String getTestJMXAuthenticatorPrincipalString() { + if ( testJMXAuthenticatorPrincipal != null ) { + return testJMXAuthenticatorPrincipal.toString(); + } + + return null; + } + + /** + * Instantiates and registers a StandardMBean in the MBean server. + * + * @param implementationClassName + * The implementation class name of the MBean. + * @param interfaceClassName + * The management interface class name of the MBean. + * @param isMXBean + * If true, the resultant MBean is an MXBean. + * @param name + * The object name of the StandardMBean. + */ + @SuppressWarnings("unchecked") + public void createStandardMBean( + String implementationClassName, + String interfaceClassName, + boolean isMXBean, + ObjectName name) + throws Exception { + + Object implementation = + Class.forName(implementationClassName).newInstance(); + Class interfaceClass = interfaceClassName == null ? null : + (Class)Class.forName(interfaceClassName); + + // Create the StandardMBean + StandardMBean standardMBean = new StandardMBean( + implementation, + interfaceClass, + isMXBean); + + // Register the StandardMBean + mbeanServer.registerMBean(standardMBean, name); + } + + /** + * Instantiates and registers a StandardMBean in the MBean server. + * The object will use standard JMX design pattern to determine + * the management interface associated with the given implementation. + */ + @SuppressWarnings("unchecked") + public void createStandardMBean( + String implementationClassName, + boolean isMXBean, + ObjectName name) + throws Exception { + + createStandardMBean(implementationClassName, null, isMXBean, name); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/query/ServerDelegateMBean.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/query/ServerDelegateMBean.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Principal; +import java.util.List; + +import javax.management.remote.JMXServiceURL ; +import javax.management.ObjectName; + +@SqeDescriptorKey("INTERFACE ServerDelegateMBean") +public interface ServerDelegateMBean { + @SqeDescriptorKey("ATTRIBUTE Address") + public void addAddress(JMXServiceURL url); + + @SqeDescriptorKey("ATTRIBUTE Address") + public List getAddresses(); + + public String getPort(); + public void setPort(String p); + + public String getJavaVersion(); + + public void sqeJmxwsCredentialsProviderCalled(); + public int getSqeJmxwsCredentialsProviderCallCount(); + + public void setJmxwsCredentialsProviderUrl(String url); + public String getJmxwsCredentialsProviderUrl(); + + public void testJMXAuthenticatorCalled(); + public int getTestJMXAuthenticatorCallCount(); + + public void setTestJMXAuthenticatorPrincipal(Principal principal); + public String getTestJMXAuthenticatorPrincipalString(); + + public void createStandardMBean( + String implementationClassName, + String interfaceClassName, + boolean isMXBean, + ObjectName name) + throws Exception; + + public void createStandardMBean( + String implementationClassName, + boolean isMXBean, + ObjectName name) + throws Exception; +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/query/SqeDescriptorKey.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/query/SqeDescriptorKey.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import javax.management.DescriptorKey; + +/** + * That annotation is usable everywhere DescriptorKey is (and even more). + * It is for use to test that you can retrieve the SqeDescriptorKey into the + * appropriate Descriptor instances as built by the JMX runtime. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface SqeDescriptorKey { + @DescriptorKey("sqeDescriptorKey") + String value(); + + // List descriptor fields that may be added or may be updated + // when retrieving an MBeanInfo using a JMXWS connection compared to the + // MBeanInfo returned by a local MBeanServer. + // The annotation format is : + // = + // The values actually handled by the test suite are : + // openType=SimpleType.VOID + @DescriptorKey("descriptorFields") + String[] descriptorFields() default {}; +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/query/SupportedQueryTypesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/query/SupportedQueryTypesTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 8058865 + * @summary Tests most of the existing query types. + * @author Olivier Lagneau + * @modules java.management + * @compile TestQuery.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SupportedQueryTypesTest -mbeanClassName TestQuery + */ + +import java.util.Map ; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Properties; +import java.lang.reflect.Method; + +import java.lang.management.ManagementFactory; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MBeanServerConnection; +import javax.management.ObjectInstance; +import javax.management.ObjectName ; +import javax.management.QueryExp; + +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; + +public class SupportedQueryTypesTest { + + protected String mbeanClassName = null; + + private MBeanServerConnection mbsc = null; + + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + SupportedQueryTypesTest test = new SupportedQueryTypesTest(); + test.run(map); + + } + + public void run(Map args) { + int errorCount = 0; + + ObjectName on = null; + ObjectName serverDelegateObjectName = null; + + JMXConnectorServer cs = null; + JMXConnector cc = null; + + System.out.println("SupportedQueryTypesTest::run: Start") ; + try { + // JMX MbeanServer used inside single VM as if remote. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + cc = JMXConnectorFactory.connect(addr); + mbsc = cc.getMBeanServerConnection(); + + + // Create and register the ServerDelegate MBean on the remote MBeanServer + String serverDelegateClassName = ServerDelegate.class.getName(); + serverDelegateObjectName = + new ObjectName("defaultDomain:class=" + serverDelegateClassName); + mbsc.createMBean(serverDelegateClassName, serverDelegateObjectName); + + // Retrieve the MBean class name + mbeanClassName = (String) args.get("-mbeanClassName") ; + on = new ObjectName("defaultDomain:class=" + mbeanClassName); + + // Create and register the MBean on the remote MBeanServer + System.out.println("SupportedQueryTypesTest::run: CREATE " + + mbeanClassName + " on the remote MBeanServer with name " + + on); + mbsc.createMBean(mbeanClassName, on); + + // Create a QueryFactory and setup which query we'll use. + QueryFactory queries = new QueryFactory(mbeanClassName); + queries.buildQueries(); + int maxIndex = queries.getSize(); + int minIndex = 1; + + // Create a reference Set to check later on + // the queryNames() results + Set referenceNameSet = new HashSet(); + referenceNameSet.add(on); + + // Create a reference Set to check later on + // the queryMBeans() results + ObjectInstance oi = new ObjectInstance(on, mbeanClassName); + Set referenceInstanceSet = + new HashSet(); + referenceInstanceSet.add(oi); + + // Perform the queryNames and queryMBeans requests + for (int i = minIndex; i <= maxIndex; i++ ) { + QueryExp query = queries.getQuery(i); + System.out.println("----"); + System.out.println("SupportedQueryTypesTest::run: Query # " + i); + System.out.println("query " + query); + errorCount += + doQueryNames(query, referenceNameSet); + errorCount += + doQueryMBeans(query, referenceInstanceSet); + } + + } catch(Exception e) { + Utils.printThrowable(e, true); + errorCount++; + + } finally { + // Do unregister the MBean + try { + if (mbsc.isRegistered(on)) { + mbsc.unregisterMBean(on); + } + if (mbsc.isRegistered(serverDelegateObjectName)) { + mbsc.unregisterMBean(serverDelegateObjectName); + } + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + } + + try { + // Close JMX Connector Client + cc.close(); + // Stop connertor server + cs.stop(); + + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + } + } + + System.out.println(""); + System.out.println("SupportedQueryTypesTest::run: Done") ; + + // Handle result + if (errorCount == 0) { + System.out.println("SupportedQueryTypesTest::run: (OK)"); + } else { + String message = "SupportedQueryTypesTest::run: (ERROR) Got " + + + errorCount + " error(s)"; + System.out.println(message); + throw new RuntimeException(message); + } + } + + + private int doQueryNames(QueryExp query, Set referenceSet) { + int errorCount = 0; + System.out.println(" <*> Perform queryNames call "); + + try { + // Call queryNames on the remote MBeanServer + Set remoteSet = mbsc.queryNames(null, query); + + // Compare the 2 Set + errorCount += checkSet(remoteSet, referenceSet); + + // Cleaning + remoteSet.clear(); + + } catch (Exception e) { + Utils.printThrowable(e, true); + errorCount++; + } + + if ( errorCount == 0 ) { + System.out.println("\t(OK)"); + } else { + System.out.println("\t(ERROR) Query failed"); + } + + return errorCount; + } + + + private int doQueryMBeans(QueryExp query, Set referenceSet) { + int errorCount = 0; + System.out.println(" <*> Perform queryMBeans call "); + + try { + // Call queryMBeans on the remote MBeanServer + Set remoteSet = mbsc.queryMBeans(null, query); + + // Compare the 2 Set + errorCount += checkSet(remoteSet, referenceSet); + + // Cleaning + remoteSet.clear(); + + } catch (Exception e) { + Utils.printThrowable(e, true); + errorCount++; + } + + if ( errorCount == 0 ) { + System.out.println("\t(OK)"); + } else { + System.out.println("\t(ERROR) Query failed"); + } + + return errorCount; + } + + /** + * Pretty print of a Set content. + * When the Set isn't empty, toString() is called on each element. + *
    The variable's name used to hold that Set is given via the setName + * parameter and used in the output. + */ + private static void printSet(Set printableSet, String setName) { + if ( printableSet.size() == 0 ) { + System.out.println("The Set " + setName + " is empty"); + } else { + System.out.println("The Set " + setName + " contains :"); + + for (Iterator it = printableSet.iterator(); it.hasNext();) { + Object elem = it.next(); + System.out.println("\t" + elem.toString()); + } + } + } + + + /** + * This method check the Set remoteSet is equal to + * the reference Set referenceSet, + * which means same size and content (order doesn't matter). + *
    It returns 0 when the check is fine, otherwise 1. + */ + private int checkSet(Set remoteSet, Set referenceSet) { + if ( ! remoteSet.equals(referenceSet) ) { + System.out.println("SupportedQueryTypesTest::checkSet:" + + " (ERROR) Set aren't as expected"); + printSet(remoteSet, "remoteSet"); + printSet(referenceSet, "referenceSet"); + return 1; + } else { + return 0; + } + } + + // Utility inner class coming from JMX Tonga test suite. + private static class Utils { + + // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property + static final String DEBUG_HEADER = "[debug] "; + + // DEBUG levels + static int selectedDebugLevel = 0; + static final int DEBUG_STANDARD = 1; + static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests + static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE; + + static void parseDebugProperties() { + int level = 0; + Properties p = System.getProperties(); + + // get selected levels + if (p.getProperty("DEBUG_STANDARD") != null) { + level |= DEBUG_STANDARD; + } + + if (p.getProperty("DEBUG_VERBOSE") != null) { + level |= DEBUG_VERBOSE; + } + + if (p.getProperty("DEBUG_ALL") != null) { + level |= DEBUG_ALL; + } + + selectedDebugLevel = level; + } + + /** + * Reproduces the original parsing and collection of test parameters + * from the DTonga JMX test suite. + * + * Collects passed args and returns them in a map(argname, value) structure, + * which will be then propagated as necessary to various called methods. + */ + static Map parseParameters(String args[]) + throws Exception { + debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start"); + HashMap map = new HashMap<>(); + + for ( int i = 0; i < args.length; i++ ) { + if ( args[i].trim().startsWith("-") ) { + if ((i+1) < args.length && !args[i+1].startsWith("-") ) { + debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with value " + + args[i+1]) ; + map.put(args[i].trim(), args[i+1].trim()) ; + } else if ((i+1) < args.length && args[i+1].startsWith("-") || + (i+1) == args.length ) { + debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with null value") ; + map.put(args[i].trim(), null) ; + } else { + System.out.println( + "TestRoot::parseParameters: (WARNING) not added in map = " + + args[i]) ; + } + } + } + + debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ; + return map ; + } + + /** + * This method is to be used in all tests to print anything + * that is temporary. + * Printing is done only when debug is activated by the property DEBUG. + * Printing depends also on the DEBUG_LEVEL property. + * Here it encapsulates a System.out.println. + */ + static void debug(int level, String line) { + if ((selectedDebugLevel & level) != 0) { + System.out.println(DEBUG_HEADER + line); + } + } + + /** + * Do print stack trace when withStack is true. + * Does try to call getTargetException() and getTargetError() then + * print embedded stacks in the case of an Exception wrapping + * another Exception or an Error. Recurse until no more wrapping + * is found. + */ + static void printThrowable(Throwable theThro, boolean withStack) { + try { + if (withStack) { + theThro.printStackTrace(System.out); + } + if (theThro instanceof Exception) { + Exception t = (Exception) theThro; + Method target = null; + String blank = " "; + try { + target = t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not + } + System.out.println(blank + t.getClass() + "==>" + t.getMessage()); + while (target != null) { + try { + t = (Exception) target.invoke(t, + (java.lang.Object[]) null); + } catch (Exception ee) { + t = null; + } + try { + if (t != null) { + blank = blank + " "; + System.out.println(blank + t.getClass() + "==>" + + t.getMessage()); + try { + target = + t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not } + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + + // We may have exceptions wrapping an Error then it is + // getTargetError that is likely to be called + try { + target = ((Exception) theThro).getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + Throwable err = theThro; + while (target != null) { + try { + err = (Error) target.invoke(err, + (java.lang.Object[]) null); + } catch (Exception ee) { + err = null; + } + try { + if (err != null) { + blank = blank + " "; + System.out.println(blank + err.getClass() + "==>" + + err.getMessage()); + if (withStack) { + err.printStackTrace(System.out); + } + try { + target = err.getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + } else { + System.out.println("Throwable is : " + theThro); + } + } catch (Throwable x) { + System.out.println("Exception : raised in printException : " + x); + } + } + } + +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/query/TestQuery.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/query/TestQuery.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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. + */ + +/** + * Class TestQuery + * MBean used for testing the types wired when using QueryExp. + * It is heavily linked to QueryFactory. + */ +public class TestQuery extends QueryData implements TestQueryMBean { + + /** + * Attribute : BooleanAtt + */ + private boolean booleanAtt = booleanValue; + + /** + * Attribute : DoubleAtt + */ + private double doubleAtt = doubleValue; + + /** + * Attribute : FloatAtt + */ + private float floatAtt = floatValue; + + /** + * Attribute : IntAtt + */ + private int intAtt = intValue; + + /** + * Attribute : IntegerAtt + */ + private Integer integerAtt = integerValue; + + /** + * Attribute : LongAtt + */ + private long longAtt = longValue; + + /** + * Attribute : StringAtt + */ + private String stringAtt = stringValue; + + public TestQuery() { + } + + /** + * Get Att of type boolean + */ + public boolean getBooleanAtt() { + return booleanAtt; + } + + /** + * Set Att of type boolean + */ + public void setBooleanAtt(boolean value) { + booleanAtt = value; + } + + /** + * Get Att of type double + */ + public double getDoubleAtt() { + return doubleAtt; + } + + /** + * Set Att of type double + */ + public void setDoubleAtt(double value) { + doubleAtt = value; + } + + /** + * Get Att of type float + */ + public float getFloatAtt() { + return floatAtt; + } + + /** + * Set Att of type float + */ + public void setFloatAtt(float value) { + floatAtt = value; + } + + /** + * Get Att of type int + */ + public int getIntAtt() { + return intAtt; + } + + /** + * Set Att of type int + */ + public void setIntAtt(int value) { + intAtt = value; + } + + /** + * Get Att of type Integer + */ + public Integer getIntegerAtt() { + return integerAtt; + } + + /** + * Set Att of type Integer + */ + public void setIntegerAtt(Integer value) { + integerAtt = value; + } + + /** + * Get Att of type long + */ + public long getLongAtt() { + return longAtt; + } + + /** + * Set Att of type long + */ + public void setLongAtt(long value) { + longAtt = value; + } + + /** + * Get Att of type String + */ + public String getStringAtt() { + return stringAtt; + } + + /** + * Set Att of type String + */ + public void setStringAtt(String value) { + stringAtt = value; + } + +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/query/TestQueryMBean.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/query/TestQueryMBean.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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. + */ + +/** + * Interface TestQueryMBean + * MBean used for testing the types wired when using QueryExp. + * It is heavily linked to QueryFactory. + */ +public interface TestQueryMBean +{ + /** + * Get Att of type boolean + */ + public boolean getBooleanAtt(); + + /** + * Set Att of type boolean + */ + public void setBooleanAtt(boolean value); + + /** + * Get Att of type double + */ + public double getDoubleAtt(); + + /** + * Set Att of type double + */ + public void setDoubleAtt(double value); + + /** + * Get Att of type float + */ + public float getFloatAtt(); + + /** + * Set Att of type float + */ + public void setFloatAtt(float value); + + /** + * Get Att of type int + */ + public int getIntAtt(); + + /** + * Set Att of type int + */ + public void setIntAtt(int value); + + /** + * Get Att of type Integer + */ + public Integer getIntegerAtt(); + + /** + * Set Att of type Integer + */ + public void setIntegerAtt(Integer value); + + /** + * Get Att of type long + */ + public long getLongAtt(); + + /** + * Set Att of type long + */ + public void setLongAtt(long value); + + /** + * Get Att of type String + */ + public String getStringAtt(); + + /** + * Set Att of type String + */ + public void setStringAtt(String value); + +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/AuthorizationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/AuthorizationTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,613 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8058865 + * @summary Checks various authentication behavior from remote jmx client + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile Simple.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedGetException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username4 -Dpassword=password4 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedGetException -expectedSetException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + */ + +import java.io.File; +import java.util.Map ; +import java.util.HashMap ; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +import java.lang.management.ManagementFactory; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory ; +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.Attribute ; +import javax.management.ObjectName ; + +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JDKToolFinder; + +public class AuthorizationTest { + + static final String SERVER_CLASS_NAME = "AuthorizationTest"; + static final String CLIENT_CLASS_NAME = "AuthorizationTest$ClientSide"; + static final String CLIENT_CLASS_MAIN = CLIENT_CLASS_NAME; + + static final String USERNAME_PROPERTY = "username"; + static final String PASSWORD_PROPERTY = "password"; + + private JMXConnectorServer cs; + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + + // Supported parameters list format is : + // "MainClass [-server ...] [-client ...] + // with either "-parami valuei" or "-parami" + HashMap serverMap = new HashMap<>() ; + int clientArgsIndex = + Utils.parseServerParameters(args, SERVER_CLASS_NAME, serverMap); + + // Extract and records client params + String[] clientParams = null; + if (clientArgsIndex < args.length) { + int clientParamsSize = args.length - clientArgsIndex; + clientParams = new String[clientParamsSize]; + System.arraycopy(args, clientArgsIndex, clientParams, 0, clientParamsSize); + } else { + clientParams = new String[0]; + } + + // Run test + AuthorizationTest test = new AuthorizationTest(); + test.run(serverMap, clientParams); + + } + + /* + * Create the MBeansServer side of the test and returns its address + */ + private JMXServiceURL createServerSide(Map serverMap) + throws Exception { + final int NINETY_SECONDS = 90; + + System.out.println("AuthorizationTest::createServerSide: Start") ; + + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + + // Creates connection environment from server side params + HashMap env = new HashMap<>(); + String value = null; + + if ((value = (String)serverMap.get("-mapType")) != null) { + if (value.contains("x.access.file")) { + String accessFileStr = System.getProperty("test.src") + + File.separator + "access.properties"; + env.put("jmx.remote.x.access.file", accessFileStr); + System.out.println("Added " + accessFileStr + " file as jmx.remote.x.access.file"); + } + if (value.contains("x.password.file")) { + String passwordFileStr = System.getProperty("test.src") + + File.separator + "password.properties"; + env.put("jmx.remote.x.password.file", passwordFileStr); + System.out.println("Added " + passwordFileStr + " file as jmx.remote.x.password.file"); + } + } + + if (serverMap.containsKey("-populate")) { + String populateClassName = "Simple"; + ObjectName on = + new ObjectName("defaultDomain:class=Simple"); + + Utils.debug(Utils.DEBUG_STANDARD, "create and register Simple MBean") ; + mbs.createMBean(populateClassName, on); + } + + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); + cs.start(); + + Utils.waitReady(cs, NINETY_SECONDS); + + JMXServiceURL addr = cs.getAddress(); + + System.out.println("AuthorizationTest::createServerSide: Done.") ; + + return addr; + } + + /* + * Creating command-line for running subprocess JVM: + * + * JVM command line is like: + * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main + * + * {defaultopts} are the default java options set by the framework. + * + */ + private List buildCommandLine(String args[]) { + List opts = new ArrayList<>(); + opts.add(JDKToolFinder.getJDKTool("java")); + opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts())); + + String usernameValue = System.getProperty(USERNAME_PROPERTY); + if (usernameValue != null) { + opts.add("-D" + USERNAME_PROPERTY + "=" + usernameValue); + } + String passwordValue = System.getProperty(PASSWORD_PROPERTY); + if (passwordValue != null) { + opts.add("-D" + PASSWORD_PROPERTY + "=" + passwordValue); + } + + opts.add("-cp"); + opts.add(System.getProperty("test.class.path", "test.class.path")); + opts.add(CLIENT_CLASS_MAIN); + opts.addAll(Arrays.asList(args)); + return opts; + } + + /** + * Runs AuthorizationTest$ClientSide with the passed options and redirects + * subprocess standard I/O to the current (parent) process. This provides a + * trace of what happens in the subprocess while it is runnning (and before + * it terminates). + * + * @param serviceUrlStr string representing the JMX service Url to connect to. + */ + private int runClientSide(String args[], String serviceUrlStr) throws Exception { + + // Building command-line + List opts = buildCommandLine(args); + opts.add("-serviceUrl"); + opts.add(serviceUrlStr); + + // Launch separate JVM subprocess + int exitCode = 0; + String[] optsArray = opts.toArray(new String[0]); + ProcessBuilder pb = new ProcessBuilder(optsArray); + Process p = ProcessTools.startProcess("AuthorizationTest$ClientSide", pb); + + // Handling end of subprocess + try { + exitCode = p.waitFor(); + if (exitCode != 0) { + System.out.println( + "Subprocess unexpected exit value of [" + exitCode + + "]. Expected 0.\n"); + } + } catch (InterruptedException e) { + System.out.println("Parent process interrupted with exception : \n " + e + " :" ); + + // Parent thread unknown state, killing subprocess. + p.destroyForcibly(); + + throw new RuntimeException( + "Parent process interrupted with exception : \n " + e + " :" ); + + } finally { + if (p.isAlive()) { + p.destroyForcibly(); + } + return exitCode; + } + + } + + public void run(Map serverArgs, String clientArgs[]) { + + System.out.println("AuthorizationTest::run: Start") ; + int errorCount = 0; + + try { + // Initialise the server side + JMXServiceURL urlToUse = createServerSide(serverArgs); + + // Run client side + errorCount = runClientSide(clientArgs, urlToUse.toString()); + + if ( errorCount == 0 ) { + System.out.println("AuthorizationTest::run: Done without any error") ; + } else { + System.out.println("AuthorizationTest::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + + cs.stop(); + + } catch(Exception e) { + throw new RuntimeException(e); + } + + } + + private static class ClientSide { + + private JMXConnector cc = null; + private MBeanServerConnection mbsc = null; + + public static void main(String args[]) throws Exception { + + // Parses parameters + Utils.parseDebugProperties(); + + // Supported parameters list format is : "MainClass [-client ...] + // with either "-parami valuei" or "-parami" + HashMap clientMap = new HashMap<>() ; + Utils.parseClientParameters(args, CLIENT_CLASS_NAME, clientMap); + + // Run test + ClientSide test = new ClientSide(); + test.run(clientMap); + + } + + public void run(Map args) { + + int errorCount = 0 ; + + try { + boolean expectedCreateException = + (args.containsKey("-expectedCreateException")) ? true : false ; + boolean expectedGetException = + (args.containsKey("-expectedGetException")) ? true : false ; + boolean expectedSetException = + (args.containsKey("-expectedSetException")) ? true : false ; + boolean expectedInvokeException = + (args.containsKey("-expectedInvokeException")) ? true : false ; + // JSR262 (see bug 6440374) + // There is no special JSR262 protocol operation for connect. + // The first request sent initiate the connection. + // In the JSR262 current implementation, getDefaultDomain is sent to + // the server in order to get the server part of the connection ID. + // => the connection may fail if no access permission on get requests. + boolean expectedConnectException = + (args.containsKey("-expectedConnectException")) ? true : false ; + // Before connection, + // remove the element of the Map with null values (not supported by RMI) + // See bug 4982668 + args.remove("-expectedCreateException"); + args.remove("-expectedGetException"); + args.remove("-expectedSetException"); + args.remove("-expectedInvokeException"); + args.remove("-expectedConnectException"); + + + // Here do connect to the JMX Server + String username = System.getProperty("username"); + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::run: CONNECT on behalf of \"" + username + "\""); + doConnect(args, expectedConnectException); + + // If the connection did not fail, perform some requests. + // At this stage the mbeanserver connection is up and running + if (mbsc != null) { + ObjectName on = new ObjectName("defaultDomain:class=Simple"); + + // Create request + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::run: CREATE on behalf of \"" + + username + "\""); + errorCount += doCreateRequest(mbsc, + new ObjectName("defaultDomain:class=Simple,user=" + username), + expectedCreateException); + + // Get request + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::run: GET on behalf of \"" + + username + "\""); + errorCount += doGetRequest(mbsc, on, expectedGetException); + + // Set request + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::run: SET on behalf of \"" + + username + "\""); + errorCount += doSetRequest(mbsc, on, expectedSetException); + + // Invoke request + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::run: INVOKE on behalf of \"" + + username + "\""); + errorCount += doInvokeRequest(mbsc, on, expectedInvokeException); + } + + } catch(Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + } finally { + // Terminate the JMX Client + try { + cc.close(); + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + } + } + + System.out.println("ClientSide::run: Done") ; + + // Handle result + if (errorCount == 0) { + System.out.println("ClientSide::run: (OK) authorization test succeeded."); + } else { + String message = "AuthorizationTest$ClientSide::run: (ERROR) " + + " authorization test failed with " + + errorCount + " error(s)"; + System.out.println(message); + throw new RuntimeException(message); + } + } + + protected void doConnect(Map args, + boolean expectedException) { + + String msgTag = "ClientSide::doConnect"; + boolean throwRuntimeException = false; + String message = ""; + + try { + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doConnect: Connect the client"); + + // Collect connection environment + HashMap env = new HashMap<>(); + + Object value = args.get("-mapType"); + if (value != null) { + String username = System.getProperty("username"); + String password = System.getProperty("password"); + Utils.debug(Utils.DEBUG_STANDARD, + msgTag + "add \"jmx.remote.credentials\" = \"" + + username + "\", \"" + password + "\""); + env.put("jmx.remote.credentials", + new String[] { username , password }); + } + + // Get a connection to remote mbean server + JMXServiceURL addr = new JMXServiceURL((String)args.get("-serviceUrl")); + cc = JMXConnectorFactory.connect(addr,env); + mbsc = cc.getMBeanServerConnection(); + + if (expectedException) { + message = "ClientSide::doConnect: (ERROR) " + + "Connect did not fail with expected SecurityException"; + System.out.println(message); + throwRuntimeException = true; + } else { + System.out.println("ClientSide::doConnect: (OK) Connect succeed"); + } + } catch(Exception e) { + Utils.printThrowable(e, true); + if (expectedException) { + if (e instanceof java.lang.SecurityException) { + System.out.println("ClientSide::doConnect: (OK) " + + "Connect failed with expected SecurityException"); + } else { + message = "ClientSide::doConnect: (ERROR) " + + "Create failed with " + e.getClass() + + " instead of expected SecurityException"; + System.out.println(message); + throwRuntimeException = true; + } + } else { + message = "ClientSide::doConnect: (ERROR) " + + "Connect failed"; + System.out.println(message); + throwRuntimeException = true; + } + } + + // If the connection failed, or if the connection succeeded but should not, + // no need to go further => throw RuntimeException and exit the test + if (throwRuntimeException) { + throw new RuntimeException(message); + } + } + + protected int doCreateRequest(MBeanServerConnection mbsc, + ObjectName on, + boolean expectedException) { + int errorCount = 0; + + try { + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doCreateRequest: Create and register the MBean") ; + + mbsc.createMBean("Simple", on) ; + + if (expectedException) { + System.out.println("ClientSide::doCreateRequest: " + + "(ERROR) Create did not fail with expected SecurityException"); + errorCount++; + } else { + System.out.println("ClientSide::doCreateRequest: (OK) Create succeed") ; + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + if (expectedException) { + if (e instanceof java.lang.SecurityException) { + System.out.println("ClientSide::doCreateRequest: " + + "(OK) Create failed with expected SecurityException") ; + } else { + System.out.println("ClientSide::doCreateRequest: " + + "(ERROR) Create failed with " + + e.getClass() + " instead of expected SecurityException"); + errorCount++; + } + } else { + System.out.println("ClientSide::doCreateRequest: " + + "(ERROR) Create failed"); + errorCount++; + } + } + return errorCount; + } + + protected int doGetRequest(MBeanServerConnection mbsc, + ObjectName on, + boolean expectedException) { + int errorCount = 0; + + try { + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doGetRequest: Get attributes of the MBean") ; + + mbsc.getAttribute(on, "Attribute"); + + if (expectedException) { + System.out.println("ClientSide::doGetRequest: " + + "(ERROR) Get did not fail with expected SecurityException"); + errorCount++; + } else { + System.out.println("ClientSide::doGetRequest: (OK) Get succeed") ; + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + if (expectedException) { + if (e instanceof java.lang.SecurityException) { + System.out.println("ClientSide::doGetRequest: " + + "(OK) Get failed with expected SecurityException") ; + } else { + System.out.println("ClientSide::doGetRequest: " + + "(ERROR) Get failed with " + + e.getClass() + " instead of expected SecurityException"); + errorCount++; + } + } else { + System.out.println("ClientSide::doGetRequest: (ERROR) Get failed"); + errorCount++; + } + } + + return errorCount; + } + + protected int doSetRequest(MBeanServerConnection mbsc, + ObjectName on, + boolean expectedException) { + int errorCount = 0; + + try { + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doSetRequest: Set attributes of the MBean") ; + + Attribute attribute = new Attribute("Attribute", "My value") ; + mbsc.setAttribute(on, attribute) ; + + if (expectedException) { + System.out.println("ClientSide::doSetRequest: " + + "(ERROR) Set did not fail with expected SecurityException"); + errorCount++; + } else { + System.out.println("ClientSide::doSetRequest: (OK) Set succeed") ; + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + if (expectedException) { + if (e instanceof java.lang.SecurityException) { + System.out.println("ClientSide::doSetRequest: " + + "(OK) Set failed with expected SecurityException") ; + } else { + System.out.println("ClientSide::doSetRequest: " + + "(ERROR) Set failed with " + + e.getClass() + " instead of expected SecurityException"); + errorCount++; + } + } else { + System.out.println("ClientSide::doSetRequest: (ERROR) Set failed"); + errorCount++; + } + } + return errorCount; + } + + protected int doInvokeRequest(MBeanServerConnection mbsc, + ObjectName on, + boolean expectedException) { + int errorCount = 0; + + try { + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doInvokeRequest: Invoke operations on the MBean") ; + + mbsc.invoke(on, "operation", null, null) ; + + if (expectedException) { + System.out.println("ClientSide::doInvokeRequest: " + + "(ERROR) Invoke did not fail with expected SecurityException"); + errorCount++; + } else { + System.out.println("ClientSide::doInvokeRequest: (OK) Invoke succeed") ; + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + if (expectedException) { + if (e instanceof java.lang.SecurityException) { + System.out.println("ClientSide::doInvokeRequest: " + + "(OK) Invoke failed with expected SecurityException") ; + } else { + System.out.println("ClientSide::doInvokeRequest: " + + " (ERROR) Invoke failed with " + + e.getClass() + " instead of expected SecurityException"); + errorCount++; + } + } else { + System.out.println("ClientSide::doInvokeRequest: " + + "(ERROR) Invoke failed"); + errorCount++; + } + } + return errorCount; + } + + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/MBS_Light.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/MBS_Light.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.AccessControlContext; +import java.security.AccessController; +import javax.security.auth.Subject; +import java.security.Principal; +import java.util.Iterator; +import java.util.Set; + +import javax.management.MBeanRegistration ; +import javax.management.MBeanServer ; +import javax.management.ObjectName ; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.Notification; + +public class MBS_Light extends NotificationBroadcasterSupport + implements MBS_LightMBean, MBeanRegistration, NotificationListener +{ + private RjmxMBeanParameter param = null ; + private String aString = "notset" ; + private int anInt = 0 ; + private MBeanServer mbs = null ; + private ObjectName objname = null ; + private Exception anException = null ; + private Error anError = null ; + private int count = 0; + private SimpleListener listener = new SimpleListener(); + + @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR MBS_Light") + public MBS_Light() { + } + + @SqeDescriptorKey("ONE RjmxMBeanParameter PARAMETER CONSTRUCTOR MBS_Light") + public MBS_Light(@SqeDescriptorKey("CONSTRUCTOR PARAMETER param") + RjmxMBeanParameter param) { + this.param = param ; + } + + @SqeDescriptorKey("ONE String PARAMETER CONSTRUCTOR MBS_Light") + public MBS_Light(@SqeDescriptorKey("CONSTRUCTOR PARAMETER param")String param) { + this.aString = param ; + } + + // Getter for property param + public RjmxMBeanParameter getParam() { + return this.param ; + } + + // Setter for property param + public void setParam(RjmxMBeanParameter param) { + this.param = param ; + } + + // Getter for property aString + public String getAstring() { + return this.aString ; + } + + // Setter for property aString + public void setAstring(String aString) { + this.aString = aString ; + } + + // Getter for property anInt + public int getAnInt() { + return this.anInt ; + } + + // Setter for property anInt + public void setAnInt(int anInt) { + this.anInt = anInt ; + } + + // Getter for property anException + public Exception getAnException() { + return this.anException ; + } + + // Setter for property anException + public void setAnException(Exception anException) { + this.anException = anException ; + } + + // Getter for property anError + public Error getAnError() { + return this.anError ; + } + + // Setter for property anError + public void setAnError(Error anError) { + this.anError = anError ; + } + + // An operation + public RjmxMBeanParameter operate1(String name) { + return new RjmxMBeanParameter(name) ; + } + + // An operation + public String operate2(RjmxMBeanParameter param) { + return param.name ; + } + + // An operation + public void throwError() { + throw new Error("JSR-160-ERROR"); + } + + // An operation + public void throwException() throws Exception { + throw new Exception("JSR-160-EXCEPTION"); + } + + // MBeanRegistration method + public void postDeregister() { + } + + // MBeanRegistration method + public void postRegister(Boolean registrationDone) { + } + + // MBeanRegistration method + public void preDeregister() + throws Exception + { + } + + // MBeanRegistration method + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception + { + this.mbs = server ; + if ( name == null ) { + this.objname = new ObjectName("protocol:class=MBS_Light") ; + } + else { + this.objname = name ; + } + return this.objname ; + } + + public synchronized void handleNotification(Notification notification, + Object handback) { + Utils.debug(Utils.DEBUG_STANDARD, + "MBS_Light::handleNotification: " + notification); + listener.handleNotification(notification, handback); + } + + // Send a notification + public void sendNotification() { + Notification notification = + new Notification("JSR160-TCK-NOTIFICATION", this, count++); + sendNotification(notification); + } + + public Object waitForNotificationHB() { + return listener.waitForNotificationHB(); + } + + // Receive multi notifications and send back handbacks + public synchronized Object[] waitForMultiNotifications(String nb) { + return listener.waitForMultiNotifications(Integer.valueOf(nb).intValue()); + } + + // Receive a notification + public synchronized String waitForNotification() { + return listener.waitForNotification(); + } + + // Is the notification received + public synchronized Boolean notificationReceived() { + return Boolean.valueOf(listener.isNotificationReceived()); + } + + // The authorization Id + public String getAuthorizationId() { + AccessControlContext acc = AccessController.getContext(); + Subject subject = Subject.getSubject(acc); + Set principals = subject.getPrincipals(); + Iterator i = principals.iterator(); + StringBuffer buffer = new StringBuffer(); + while(i.hasNext()) { + Principal p = i.next(); + buffer.append(p.getName()); + if(i.hasNext()) + buffer.append(" "); + } + + return buffer.toString(); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/MBS_LightMBean.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/MBS_LightMBean.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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. + */ + +@SqeDescriptorKey("INTERFACE MBS_LightMBean") +public interface MBS_LightMBean { + // Getter for property param + @SqeDescriptorKey("ATTRIBUTE Param") + public RjmxMBeanParameter getParam() ; + + // Setter for property param + @SqeDescriptorKey("ATTRIBUTE Param") + public void setParam(RjmxMBeanParameter param) ; + + // Getter for property aString + @SqeDescriptorKey("ATTRIBUTE Astring") + public String getAstring() ; + + // Setter for property aString + @SqeDescriptorKey("ATTRIBUTE Astring") + public void setAstring(String aString) ; + + // Getter for property anInt + @SqeDescriptorKey("ATTRIBUTE AnInt") + public int getAnInt() ; + + // Setter for property anInt + @SqeDescriptorKey("ATTRIBUTE AnInt") + public void setAnInt(int anInt) ; + + // Getter for property anException + @SqeDescriptorKey("ATTRIBUTE AnException") + public Exception getAnException() ; + + // Setter for property anException + @SqeDescriptorKey("ATTRIBUTE AnException") + public void setAnException(Exception anException) ; + + // Getter for property anError + @SqeDescriptorKey("ATTRIBUTE AnError") + public Error getAnError() ; + + // Setter for property anError + @SqeDescriptorKey("ATTRIBUTE AnError") + public void setAnError(Error anError) ; + + // An operation + @SqeDescriptorKey("OPERATION operate1") + public RjmxMBeanParameter operate1( + @SqeDescriptorKey("OPERATION PARAMETER name")String name) ; + + // An operation + @SqeDescriptorKey("OPERATION operate2") + public String operate2( + @SqeDescriptorKey("OPERATION PARAMETER param")RjmxMBeanParameter param) ; + + // Throws an error + @SqeDescriptorKey("OPERATION throwError") + public void throwError(); + + // Throws an exception + @SqeDescriptorKey("OPERATION throwException") + public void throwException() throws Exception; + + // Send a notification + @SqeDescriptorKey("OPERATION sendNotification") + public void sendNotification(); + + // Receive a notification and return the type + @SqeDescriptorKey("OPERATION waitForNotification") + public String waitForNotification(); + + // Receive a notification and return the HandBack + @SqeDescriptorKey("OPERATION waitForNotificationHB") + public Object waitForNotificationHB(); + + // Receive multi notifications and return the HandBacks + @SqeDescriptorKey("OPERATION waitForMultiNotifications") + public Object[] waitForMultiNotifications( + @SqeDescriptorKey("OPERATION PARAMETER nb")String nb); + + // Is the notification received + @SqeDescriptorKey("OPERATION notificationReceived") + public Boolean notificationReceived(); + + // Return the current authorization Id + @SqeDescriptorKey("OPERATION getAuthorizationId") + public String getAuthorizationId(); +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/RjmxMBeanParameter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/RjmxMBeanParameter.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Serializable ; + +/** + * That class is used to modelize a parameter to be used as MBean property + * value or MBean operation parameter or returned value. + */ +public class RjmxMBeanParameter implements Serializable { + public String name = "unset" ; + + public RjmxMBeanParameter() { + } + + public RjmxMBeanParameter(String name) { + this.name = name ; + } + + public boolean equals(Object obj) { + if ( this.name.equals(((RjmxMBeanParameter)obj).name) ) { + return true ; + } else { + return false ; + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/SecurityTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/SecurityTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,800 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8058865 + * @summary Checks various secure ways of connecting from remote jmx client + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile MBS_Light.java ServerDelegate.java TestSampleLoginModule.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=SQE_username -Dpassword=SQE_password SecurityTest -server -mapType x.password.file -client -mapType credentials + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=UNKNOWN_username -Dpassword=SQE_password SecurityTest -server -mapType x.password.file -client -mapType credentials -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=SQE_username -Dpassword=WRONG_password SecurityTest -server -mapType x.password.file -client -mapType credentials -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dsusername=TestJMXAuthenticatorUsername -Dspassword=TestJMXAuthenticatorPassword -Dusername=TestJMXAuthenticatorUsername -Dpassword=TestJMXAuthenticatorPassword SecurityTest -server -mapType x.authenticator -client -mapType credentials + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dsusername=TestJMXAuthenticatorUsername -Dspassword=TestJMXAuthenticatorPassword -Dusername=AnotherTestJMXAuthenticatorUsername -Dpassword=TestJMXAuthenticatorPassword SecurityTest -server -mapType x.authenticator -client -mapType credentials -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.PasswordFileAuthentication -client -mapType credentials + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config.UNKNOWN -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.PasswordFileAuthentication -client -mapType credentialss -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.UnknownAuthentication -client -mapType credentials -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dsusername=usernameSampleLoginModule -Dspassword=passwordSampleLoginModule -Dpassword.file=password.properties -Dusername=usernameSampleLoginModule -Dpassword=passwordSampleLoginModule SecurityTest -server -mapType x.login.config.SampleLoginModule -client -mapType credentials + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dsusername=usernameSampleLoginModule -Dspassword=passwordSampleLoginModule -Dpassword.file=password.properties -Dusername=AnotherUsernameSampleLoginModule -Dpassword=passwordSampleLoginModule SecurityTest -server -mapType x.login.config.SampleLoginModule -client -mapType credentials -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword WRONG_password -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -keystore keystoreClient -keystorepassword glopglop -truststore truststoreClient -truststorepassword glopglop + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -keystore keystoreClient -keystorepassword WRONG_password -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -client -keystore keystoreClient -keystorepassword glopglop -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_MD5 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.md5 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_SHA SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.md5 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_MD5 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.sha -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=SSLv3 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.sslv3 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=TLSv1 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.sslv3 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=SSLv3 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.tlsv1 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + */ + +import java.io.File; +import java.util.Map ; +import java.util.HashMap ; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory ; +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.Attribute ; +import javax.management.ObjectName ; + +import javax.rmi.ssl.SslRMIClientSocketFactory; +import javax.rmi.ssl.SslRMIServerSocketFactory; + +import java.security.Security; + +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JDKToolFinder; + +public class SecurityTest { + + static final String SERVER_CLASS_NAME = "SecurityTest"; + static final String CLIENT_CLASS_NAME = "SecurityTest$ClientSide"; + static final String CLIENT_CLASS_MAIN = CLIENT_CLASS_NAME; + + static final String USERNAME_PROPERTY = "username"; + static final String PASSWORD_PROPERTY = "password"; + + static final String SERVER_DELEGATE_MBEAN_NAME = + "defaultDomain:class=ServerDelegate"; + + static final String RMI_SERVER_SOCKET_FACTORY_SSL = "rmi.server.socket.factory.ssl"; + static final String RMI_CLIENT_SOCKET_FACTORY_SSL = "rmi.client.socket.factory.ssl"; + static final String KEYSTORE_PROPNAME = "javax.net.ssl.keyStore"; + static final String KEYSTORE_PWD_PROPNAME = "javax.net.ssl.keyStorePassword"; + static final String TRUSTSTORE_PROPNAME = "javax.net.ssl.trustStore"; + static final String TRUSTSTORE_PWD_PROPNAME = "javax.net.ssl.trustStorePassword"; + + static final String RMI_SSL_CLIENT_ENABLEDCIPHERSUITES = + "javax.rmi.ssl.client.enabledCipherSuites"; + static final String RMI_SSL_CLIENT_ENABLEDPROTOCOLS = + "javax.rmi.ssl.client.enabledProtocols"; + + private JMXConnectorServer cs; + + // Construct and set keyStore properties from given map + static void setKeyStoreProperties(Map map) { + + String keyStore = (String) map.get("-keystore"); + keyStore = buildSourcePath(keyStore); + System.setProperty(KEYSTORE_PROPNAME, keyStore); + System.out.println("keyStore location = \"" + keyStore + "\""); + + String password = (String) map.get("-keystorepassword"); + System.setProperty(KEYSTORE_PWD_PROPNAME, password); + System.out.println("keyStore password = " + password); + + } + + // Construct and set trustStore properties from given map + static void setTrustStoreProperties(Map map) { + + String trustStore = (String) map.get("-truststore"); + trustStore = buildSourcePath(trustStore); + System.setProperty(TRUSTSTORE_PROPNAME, trustStore); + System.out.println("trustStore location = \"" + trustStore + "\""); + + String password = (String) map.get("-truststorepassword"); + System.setProperty(TRUSTSTORE_PWD_PROPNAME, password); + System.out.println("trustStore password = " + password); + + } + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + + // Supported parameters list format is : + // "MainClass [-server ...] [-client ...] + // with either "-parami valuei" or "-parami" + HashMap serverMap = new HashMap<>() ; + int clientArgsIndex = + Utils.parseServerParameters(args, SERVER_CLASS_NAME, serverMap); + + // Extract and records client params + String[] clientParams = null; + if (clientArgsIndex < args.length) { + int clientParamsSize = args.length - clientArgsIndex; + clientParams = new String[clientParamsSize]; + System.arraycopy(args, clientArgsIndex, clientParams, 0, clientParamsSize); + } else { + clientParams = new String[0]; + } + + // Run test + SecurityTest test = new SecurityTest(); + test.run(serverMap, clientParams); + + } + + // Return full path of filename in the test sopurce directory + private static String buildSourcePath(String filename) { + return System.getProperty("test.src") + File.separator + filename; + } + + /* + * Collects security run params for server side. + */ + private HashMap setServerSecurityEnv(Map map) + throws Exception { + + // Creates Authentication environment from server side params + HashMap env = new HashMap<>(); + + // Retrieve and set keystore and truststore config if any + if (map.containsKey("-keystore") && + map.get("-keystore") != null) { + setKeyStoreProperties(map); + } + System.out.println("Done keystore properties"); + + if (map.containsKey("-truststore") && + map.get("-truststore") != null) { + setTrustStoreProperties(map); + } + System.out.println("Done truststore properties"); + + String value = null; + if ((value = (String)map.get("-mapType")) != null) { + + // Case of remote password file with all authorized credentials + if (value.contains("x.password.file")) { + String passwordFileStr = buildSourcePath("password.properties"); + env.put("jmx.remote.x.password.file", passwordFileStr); + System.out.println("Added " + passwordFileStr + + " file as jmx.remote.x.password.file"); + } + + // Case of dedicated authenticator class : TestJMXAuthenticator + if (value.contains("x.authenticator")) { + env.put("jmx.remote.authenticator", new TestJMXAuthenticator()) ; + System.out.println( + "Added \"jmx.remote.authenticator\" = TestJMXAuthenticator"); + } + + // Case of security config file with standard Authentication + if (value.contains("x.login.config.PasswordFileAuthentication")) { + String loginConfig = System.getProperty("login.config.file"); + + // Override the default JAAS configuration + System.setProperty("java.security.auth.login.config", + "file:" + loginConfig); + System.out.println("Overrided default JAAS configuration with " + + "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ; + + env.put("jmx.remote.x.login.config", "PasswordFileAuthentication") ; + System.out.println( + "Added \"jmx.remote.x.login.config\" = " + + "\"PasswordFileAuthentication\"") ; + + // redirects "password.file" property to file in ${test.src} + String passwordFileStr = + buildSourcePath(System.getProperty("password.file")); + System.setProperty("password.file", passwordFileStr); + System.out.println( + "Redirected \"password.file\" property value to = " + + passwordFileStr) ; + } + + // Case of security config file with unexisting athentication config + if (value.contains("x.login.config.UnknownAuthentication")) { + String loginConfig = System.getProperty("login.config.file"); + + // Override the default JAAS configuration + System.setProperty("java.security.auth.login.config", + "file:" + loginConfig); + System.out.println("Overrided default JAAS configuration with " + + "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ; + + env.put("jmx.remote.x.login.config", "UnknownAuthentication") ; + System.out.println( + "Added \"jmx.remote.x.login.config\" = " + + "\"UnknownAuthentication\"") ; + + // redirects "password.file" property to file in ${test.src} + String passwordFileStr = + buildSourcePath(System.getProperty("password.file")); + System.setProperty("password.file", passwordFileStr); + System.out.println( + "Redirected \"password.file\" property value to = " + + passwordFileStr) ; + } + + // Case of security config file with dedicated login module + if (value.contains("x.login.config.SampleLoginModule")) { + String loginConfig = System.getProperty("login.config.file"); + + // Override the default JAAS configuration + System.setProperty("java.security.auth.login.config", + "file:" + loginConfig); + System.out.println("Overrided default JAAS configuration with " + + "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ; + + env.put("jmx.remote.x.login.config", "SampleLoginModule") ; + System.out.println( + "Added \"jmx.remote.x.login.config\" = " + + "\"SampleLoginModule\"") ; + } + + // Simple rmi ssl authentication + if (value.contains(RMI_CLIENT_SOCKET_FACTORY_SSL)) { + env.put("jmx.remote.rmi.client.socket.factory", + new SslRMIClientSocketFactory()) ; + System.out.println( + "Added \"jmx.remote.rmi.client.socket.factory\"" + + " = SslRMIClientSocketFactory") ; + } + + if (value.contains(RMI_SERVER_SOCKET_FACTORY_SSL)) { + if (value.contains( + "rmi.server.socket.factory.ssl.need.client.authentication")) { + // rmi ssl authentication with client authentication + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory(null, null, true)) ; + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory with client authentication") ; + + } else if (value.contains("rmi.server.socket.factory.ssl.enabled.cipher.suites.md5")) { + // Allows all ciphering and protocols for testing purpose + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory( + new String[] {"SSL_RSA_WITH_RC4_128_MD5"}, null, false)); + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory with SSL_RSA_WITH_RC4_128_MD5 cipher suite"); + + } else if (value.contains("rmi.server.socket.factory.ssl.enabled.cipher.suites.sha")) { + // Allows all ciphering and protocols for testing purpose + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory( + new String[] { "SSL_RSA_WITH_RC4_128_SHA" }, null, false)) ; + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory with SSL_RSA_WITH_RC4_128_SHA cipher suite") ; + + } else if (value.contains("rmi.server.socket.factory.ssl.enabled.protocols.sslv3")) { + // Allows all ciphering and protocols for testing purpose + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory(null, new String[] {"SSLv3"}, false)) ; + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory with SSLv3 protocol") ; + + } else if (value.contains("rmi.server.socket.factory.ssl.enabled.protocols.tlsv1")) { + // Allows all ciphering and protocols for testing purpose + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory(null, new String[] {"TLSv1"}, false)) ; + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory with TLSv1 protocol") ; + + } else { + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory()); + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory"); + } + } + } + + return env; + } + + /* + * Create the MBeansServer side of the test and returns its address + */ + private JMXServiceURL createServerSide(Map serverMap) + throws Exception { + final int NINETY_SECONDS = 90; + + System.out.println("SecurityTest::createServerSide: Start") ; + + // Prepare server side security env + HashMap env = setServerSecurityEnv(serverMap); + + // Create and start mbean server and connector server + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); + cs.start(); + + // Waits availibility of connector server + Utils.waitReady(cs, NINETY_SECONDS); + + JMXServiceURL addr = cs.getAddress(); + + System.out.println("SecurityTest::createServerSide: Done.") ; + + return addr; + } + + /* + * Creating command-line for running subprocess JVM: + * + * JVM command line is like: + * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main + * + * {defaultopts} are the default java options set by the framework. + * + */ + private List buildCommandLine(String args[]) { + + System.out.println("SecurityTest::buildCommandLine: Start") ; + + List opts = new ArrayList<>(); + opts.add(JDKToolFinder.getJDKTool("java")); + opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts())); + + // We need to forward some properties to the client side + opts.add("-Dtest.src=" + System.getProperty("test.src")); + + String usernameValue = System.getProperty(USERNAME_PROPERTY); + if (usernameValue != null) { + System.out.println("SecurityTest::buildCommandLine: "+ + " forward username property to client side"); + opts.add("-D" + USERNAME_PROPERTY + "=" + usernameValue); + } + String passwordValue = System.getProperty(PASSWORD_PROPERTY); + if (passwordValue != null) { + System.out.println("SecurityTest::buildCommandLine: "+ + " forward password property to client side"); + opts.add("-D" + PASSWORD_PROPERTY + "=" + passwordValue); + } + + String enabledCipherSuites = + System.getProperty(RMI_SSL_CLIENT_ENABLEDCIPHERSUITES); + if (enabledCipherSuites != null) { + System.out.println("SecurityTest::buildCommandLine: "+ + " forward enabledCipherSuites property to client side"); + opts.add("-D" + RMI_SSL_CLIENT_ENABLEDCIPHERSUITES + + "=" + enabledCipherSuites); + } + + String enabledProtocols = + System.getProperty(RMI_SSL_CLIENT_ENABLEDPROTOCOLS); + if (enabledProtocols != null) { + System.out.println("SecurityTest::buildCommandLine: "+ + " forward enabledProtocols property to client side"); + opts.add("-D" + RMI_SSL_CLIENT_ENABLEDPROTOCOLS + + "=" + enabledProtocols); + } + + opts.add("-cp"); + opts.add(System.getProperty("test.class.path", "test.class.path")); + opts.add(CLIENT_CLASS_MAIN); + opts.addAll(Arrays.asList(args)); + + System.out.println("SecurityTest::buildCommandLine: Done.") ; + + return opts; + } + + /** + * Runs SecurityTest$ClientSide with the passed options and redirects + * subprocess standard I/O to the current (parent) process. This provides a + * trace of what happens in the subprocess while it is runnning (and before + * it terminates). + * + * @param serviceUrlStr string representing the JMX service Url to connect to. + */ + private int runClientSide(String args[], String serviceUrlStr) throws Exception { + + System.out.println("SecurityTest::runClientSide: Start") ; + + // Building command-line + List opts = buildCommandLine(args); + opts.add("-serviceUrl"); + opts.add(serviceUrlStr); + + // Launch separate JVM subprocess + int exitCode = 0; + String[] optsArray = opts.toArray(new String[0]); + ProcessBuilder pb = new ProcessBuilder(optsArray); + Process p = ProcessTools.startProcess("SecurityTest$ClientSide", pb); + + // Handling end of subprocess + try { + exitCode = p.waitFor(); + if (exitCode != 0) { + System.out.println( + "Subprocess unexpected exit value of [" + exitCode + + "]. Expected 0.\n"); + } + } catch (InterruptedException e) { + System.out.println("Parent process interrupted with exception : \n " + e + " :" ); + + // Parent thread unknown state, killing subprocess. + p.destroyForcibly(); + + throw new RuntimeException( + "Parent process interrupted with exception : \n " + e + " :" ); + + } finally { + if (p.isAlive()) { + p.destroyForcibly(); + } + + System.out.println("SecurityTest::runClientSide: Done") ; + + return exitCode; + } + + } + + public void run(Map serverArgs, String clientArgs[]) { + + System.out.println("SecurityTest::run: Start") ; + int errorCount = 0; + + try { + // Initialise the server side + JMXServiceURL urlToUse = createServerSide(serverArgs); + + // Run client side + errorCount = runClientSide(clientArgs, urlToUse.toString()); + + if ( errorCount == 0 ) { + System.out.println("SecurityTest::run: Done without any error") ; + } else { + System.out.println( + "SecurityTest::run: Done with " + errorCount + " error(s)"); + throw new RuntimeException("errorCount = " + errorCount); + } + + cs.stop(); + + } catch(Exception e) { + throw new RuntimeException(e); + } + + } + + private static class ClientSide { + + private JMXConnector cc = null; + private MBeanServerConnection mbsc = null; + + public static void main(String args[]) throws Exception { + + // Parses parameters + Utils.parseDebugProperties(); + + // Supported parameters list format is : "MainClass [-client ...] + // with either "-parami valuei" or "-parami" + HashMap clientMap = new HashMap<>() ; + Utils.parseClientParameters(args, CLIENT_CLASS_NAME, clientMap); + + // Run test + ClientSide test = new ClientSide(); + test.run(clientMap); + } + + public void run(Map args) { + + System.out.println("ClientSide::run: Start"); + int errorCount = 0; + + try { + // Setup client side parameters + HashMap env = new HashMap<>(); + + // If needed allows all ciphering and protocols for testing purpose + if (System.getProperty(RMI_SSL_CLIENT_ENABLEDCIPHERSUITES) != null) { + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + } + + // If needed allows all ciphering and protocols for testing purpose + if (System.getProperty(RMI_SSL_CLIENT_ENABLEDPROTOCOLS) != null) { + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + } + + // Retrieve and set keystore and truststore config if any + if (args.containsKey("-keystore") && + args.get("-keystore") != null) { + SecurityTest.setKeyStoreProperties(args); + } + if (args.containsKey("-truststore") && + args.get("-truststore") != null) { + SecurityTest.setTrustStoreProperties(args); + } + + Object value = args.get("-mapType"); + if ((value != null) && + value.equals("credentials")) { + String username = System.getProperty("username"); + String password = System.getProperty("password"); + Utils.debug(Utils.DEBUG_STANDARD, + "add \"jmx.remote.credentials\" = \"" + + username + "\", \"" + password + "\""); + env.put("jmx.remote.credentials", + new String[] { username , password }); + } + + String expectedThrowable = (String) args.get("-expectedThrowable"); + + String authCallCountName = "-expectedAuthenticatorCallCount"; + int authCallCountValue = 0; + if (args.containsKey(authCallCountName)) { + authCallCountValue = + (new Integer((String) args.get(authCallCountName))).intValue(); + } + + try { + // Get a connection to remote mbean server + JMXServiceURL addr = new JMXServiceURL((String)args.get("-serviceUrl")); + cc = JMXConnectorFactory.connect(addr,env); + mbsc = cc.getMBeanServerConnection(); + + // In case we should have got an exception + if (expectedThrowable != null) { + System.out.println("ClientSide::run: (ERROR) " + + " Connect did not fail with expected " + expectedThrowable); + errorCount++; + } else { + System.out.println("ClientSide::run: (OK) Connect succeed"); + } + } catch (Throwable e) { + Utils.printThrowable(e, true); + if (expectedThrowable != null) { + if (Utils.compareThrowable(e, expectedThrowable)) { + System.out.println("ClientSide::run: (OK) " + + "Connect failed with expected " + expectedThrowable); + } else { + System.out.println("ClientSide::run: (ERROR) Connect failed with " + + e.getClass() + " instead of expected " + + expectedThrowable); + errorCount++; + } + } else { + System.out.println("ClientSide::run: (ERROR) " + + "Connect failed with exception"); + errorCount++; + } + } + + // Depending on the client state, + // perform some requests + if (mbsc != null && errorCount == 0) { + // Perform some little JMX requests + System.out.println("ClientSide::run: Start sending requests"); + + doRequests(); + + // In case authentication has been used we check how it did. + if (authCallCountValue != 0) { + errorCount += checkAuthenticator(mbsc, authCallCountValue); + } + } + } catch (Exception e) { + Utils.printThrowable(e, true); + errorCount++; + } finally { + // Terminate the JMX Client if any + if (cc != null) { + try { + cc.close(); + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + } + } + } + + System.out.println("ClientSide::run: Done"); + + // Handle result + if (errorCount != 0) { + throw new RuntimeException(); + } + } + + private void doRequests() throws Exception { + + // Send some requests to the remote JMX server + ObjectName objName1 = + new ObjectName("TestDomain:class=MBS_Light,rank=1"); + String mbeanClass = "MBS_Light"; + Exception exception = new Exception("MY TEST EXCEPTION"); + Attribute attException = new Attribute("AnException", exception); + Error error = new Error("MY TEST ERROR"); + Attribute attError = new Attribute("AnError", error); + String opParamString = "TOTORO"; + RjmxMBeanParameter opParam = new RjmxMBeanParameter(opParamString); + Object[] params1 = {opParamString}; + String[] sig1 = {"java.lang.String"}; + Object[] params2 = {opParam}; + String[] sig2 = {"RjmxMBeanParameter"}; + + // Create and register the MBean + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doRequests: Create and register the MBean"); + mbsc.createMBean(mbeanClass, objName1); + if (!mbsc.isRegistered(objName1)) { + throw new Exception("Unable to register an MBean"); + } + + // Set attributes of the MBean + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doRequests: Set attributes of the MBean"); + mbsc.setAttribute(objName1, attException); + mbsc.setAttribute(objName1, attError); + + // Get attributes of the MBean + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doRequests: Get attributes of the MBean"); + Exception retException = + (Exception) mbsc.getAttribute(objName1,"AnException"); + if (!retException.getMessage().equals(exception.getMessage())) { + System.out.println("Expected = " + exception); + System.out.println("Got = " + retException); + throw new Exception("Attribute AnException not as expected"); + } + Error retError = (Error) mbsc.getAttribute(objName1, "AnError"); + if (!retError.getMessage().equals(error.getMessage())) { + System.out.println("Expected = " + error); + System.out.println("Got = " + retError); + throw new Exception("Attribute AnError not as expected"); + } + + // Invoke operations on the MBean + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doRequests: Invoke operations on the MBean"); + RjmxMBeanParameter res1 = + (RjmxMBeanParameter) mbsc.invoke(objName1, "operate1", params1, sig1); + if (!res1.equals(opParam)) { + System.out.println("Expected = " + opParam); + System.out.println("Got = " + res1); + throw new Exception("Operation operate1 behaved badly"); + } + String res2 = + (String) mbsc.invoke(objName1, "operate2", params2, sig2); + if (!res2.equals(opParamString)) { + System.out.println("Expected = " + opParamString); + System.out.println("Got = " + res2); + throw new Exception("Operation operate2 behaved badly"); + } + + // Unregister the MBean + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doRequests: Unregister the MBean"); + mbsc.unregisterMBean(objName1); + if (mbsc.isRegistered(objName1)) { + throw new Exception("Unable to unregister an MBean"); + } + } + + /** + * Make some check about the instance of TestJMXAuthenticator. + * The authenticator is supposed to have set some properties on + * a ServerDelegate MBean. + * We compare the number of times it has been called with the expected value. + * We also check the Principal that has been given to the authenticator + * was not null. + * That method is of use to authentication with the JSR 262. + * @param mbs + * @param expectedAuthenticatorCallCount + * @return The number of errors encountered. + * @throws java.lang.Exception + */ + protected int checkAuthenticator(MBeanServerConnection mbs, + int expectedAuthenticatorCallCount) throws Exception { + int errorCount = 0; + + // Ensure the authenticator has been called the right number + // of times. + int callCount = + ((Integer) mbs.getAttribute( + new ObjectName(SERVER_DELEGATE_MBEAN_NAME), + "TestJMXAuthenticatorCallCount")).intValue(); + + if (callCount == expectedAuthenticatorCallCount) { + System.out.println("---- OK Authenticator has been called " + + expectedAuthenticatorCallCount + " time"); + } else { + errorCount++; + System.out.println("---- ERROR Authenticator has been called " + callCount + + " times in place of " + expectedAuthenticatorCallCount); + } + + // Ensure the provider has been called with + // a non null Principal. + String principalString = + (String) mbs.getAttribute( + new ObjectName(SERVER_DELEGATE_MBEAN_NAME), + "TestJMXAuthenticatorPrincipalString"); + + if (principalString == null) { + errorCount++; + System.out.println("---- ERROR Authenticator has been called" + + " with a null Principal"); + } else { + if (principalString.length() > 0) { + System.out.println("---- OK Authenticator has been called" + + " with the Principal " + principalString); + } else { + errorCount++; + System.out.println("---- ERROR Authenticator has been called" + + " with an empty Principal"); + } + } + + return errorCount; + } + + } + +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/ServerDelegate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/ServerDelegate.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import javax.management.remote.JMXServiceURL ; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.StandardMBean; + +/** + * This class defines an MBean that can be registered and used on client side + * to handle informations or properties of the remote server. + * + * For example, this MBean can store IOR addresses + * of RMI/IIOP connector(s) used in a test. + * + * That MBean might not be used for testing purpose itself. + */ +public class ServerDelegate implements ServerDelegateMBean, MBeanRegistration { + + private MBeanServer mbeanServer = null; + private List addresses = null; + private String port; + private static String javaVersion = System.getProperty("java.version"); + private int sqeJmxwsCredentialsProviderCallCount = 0; + private String jmxwsCredentialsProviderUrl = null; + private int testJMXAuthenticatorCallCount = 0; + private Principal testJMXAuthenticatorPrincipal = null; + + @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR ServerDelegate") + public ServerDelegate() { + addresses = new ArrayList(); + } + + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + // Initialize MBeanServer attribute + mbeanServer = server; + return name; + } + public void postRegister(Boolean registrationDone) { + } + public void preDeregister() throws Exception { + } + public void postDeregister() { + } + + public void addAddress(JMXServiceURL url) { + addresses.add(url) ; + } + + public List getAddresses() { + return addresses ; + } + + public void setPort(String p) { + port = p ; + } + + public String getPort() { + return port ; + } + + public String getJavaVersion() { + return javaVersion; + } + + public void sqeJmxwsCredentialsProviderCalled() { + sqeJmxwsCredentialsProviderCallCount++; + } + + public int getSqeJmxwsCredentialsProviderCallCount() { + return sqeJmxwsCredentialsProviderCallCount; + } + + public void setJmxwsCredentialsProviderUrl(String url) { + jmxwsCredentialsProviderUrl = url; + } + + public String getJmxwsCredentialsProviderUrl() { + return jmxwsCredentialsProviderUrl; + } + + public void testJMXAuthenticatorCalled() { + testJMXAuthenticatorCallCount++; + } + + public int getTestJMXAuthenticatorCallCount() { + return testJMXAuthenticatorCallCount; + } + + public void setTestJMXAuthenticatorPrincipal(Principal principal) { + testJMXAuthenticatorPrincipal = principal; + } + + public String getTestJMXAuthenticatorPrincipalString() { + if ( testJMXAuthenticatorPrincipal != null ) { + return testJMXAuthenticatorPrincipal.toString(); + } + + return null; + } + + /** + * Instantiates and registers a StandardMBean in the MBean server. + * + * @param implementationClassName + * The implementation class name of the MBean. + * @param interfaceClassName + * The management interface class name of the MBean. + * @param isMXBean + * If true, the resultant MBean is an MXBean. + * @param name + * The object name of the StandardMBean. + */ + @SuppressWarnings("unchecked") + public void createStandardMBean( + String implementationClassName, + String interfaceClassName, + boolean isMXBean, + ObjectName name) + throws Exception { + + Object implementation = + Class.forName(implementationClassName).newInstance(); + Class interfaceClass = interfaceClassName == null ? null : + (Class)Class.forName(interfaceClassName); + + // Create the StandardMBean + StandardMBean standardMBean = new StandardMBean( + implementation, + interfaceClass, + isMXBean); + + // Register the StandardMBean + mbeanServer.registerMBean(standardMBean, name); + } + + /** + * Instantiates and registers a StandardMBean in the MBean server. + * The object will use standard JMX design pattern to determine + * the management interface associated with the given implementation. + */ + @SuppressWarnings("unchecked") + public void createStandardMBean( + String implementationClassName, + boolean isMXBean, + ObjectName name) + throws Exception { + + createStandardMBean(implementationClassName, null, isMXBean, name); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/ServerDelegateMBean.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/ServerDelegateMBean.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Principal; +import java.util.List; + +import javax.management.remote.JMXServiceURL ; +import javax.management.ObjectName; + +@SqeDescriptorKey("INTERFACE ServerDelegateMBean") +public interface ServerDelegateMBean { + @SqeDescriptorKey("ATTRIBUTE Address") + public void addAddress(JMXServiceURL url); + + @SqeDescriptorKey("ATTRIBUTE Address") + public List getAddresses(); + + public String getPort(); + public void setPort(String p); + + public String getJavaVersion(); + + public void sqeJmxwsCredentialsProviderCalled(); + public int getSqeJmxwsCredentialsProviderCallCount(); + + public void setJmxwsCredentialsProviderUrl(String url); + public String getJmxwsCredentialsProviderUrl(); + + public void testJMXAuthenticatorCalled(); + public int getTestJMXAuthenticatorCallCount(); + + public void setTestJMXAuthenticatorPrincipal(Principal principal); + public String getTestJMXAuthenticatorPrincipalString(); + + public void createStandardMBean( + String implementationClassName, + String interfaceClassName, + boolean isMXBean, + ObjectName name) + throws Exception; + + public void createStandardMBean( + String implementationClassName, + boolean isMXBean, + ObjectName name) + throws Exception; +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/Simple.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/Simple.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.beans.ConstructorProperties; +import javax.management.ConstructorParameters; + +/** + * This class defines a simple standard MBean. + */ +public class Simple implements SimpleMBean { + + private String attribute = "initial_value"; + private boolean operationInvoked = false; + private boolean operation2Invoked = false; + + @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR Simple") + public Simple() { + } + + @SqeDescriptorKey("TWO PARAMETERS CONSTRUCTOR Simple") + @ConstructorParameters({"unused1", "unused2"}) + public Simple(@SqeDescriptorKey("CONSTRUCTOR PARAMETER unused1")int unused1, + @SqeDescriptorKey("CONSTRUCTOR PARAMETER unused2")int unused2) { + } + + public String getAttribute() { + return attribute; + } + public void setAttribute(String s) { + attribute = s; + } + public boolean getOperationInvoked() { + return operationInvoked; + } + public boolean getOperation2Invoked() { + return operation2Invoked; + } + + public void operation() { + operationInvoked = true; + return; + } + + public String operation2(int i) { + operation2Invoked = true; + return String.valueOf(i); + } + + public void reset() { + attribute = "initial_value"; + operationInvoked = false; + operation2Invoked = false; + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/SimpleListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/SimpleListener.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// JDK +import java.util.Vector; + +// JMX +import javax.management.NotificationListener; +import javax.management.Notification; + +public class SimpleListener implements NotificationListener { + private boolean received = false; + private String type = null; + private Object handback = null; + private Vector handbacks = new Vector(); + private int nbrec = 0; + + public synchronized void handleNotification(Notification notification, + Object handback) { + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::handleNotification :" + notification); + try { + received = true; + type = notification.getType(); + this.handback = handback; + handbacks.add(handback); + nbrec++; + notify(); + } catch(Exception e) { + System.out.println("(ERROR) SimpleListener::handleNotification :" + + " Caught exception " + + e) ; + } + } + + public synchronized boolean isNotificationReceived() { + boolean ret = received; + reset(); + return ret; + } + + public synchronized Object[] waitForMultiNotifications(int nb) { + while(true) { + if(nbrec < nb) { + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForMultiNotifications wait"); + try { + wait(); + } catch(InterruptedException ie) { + // OK : we wait for being interrupted + } + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForMultiNotifications wait over"); + } + else + break; + } + Object[] ret = handbacks.toArray(); + reset(); + return ret; + } + + private void reset() { + received = false; + handback = null; + handbacks.removeAllElements(); + type = null; + } + + public synchronized Object waitForNotificationHB() { + while(true) { + if(!received) { + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForNotificationHB wait"); + try { + wait(); + } catch(InterruptedException ie) { + // OK : we wait for being interrupted + } + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForNotificationHB received"); + } + else + break; + } + Object ret = handback; + reset(); + return ret; + } + + public synchronized String waitForNotification() { + while(true) { + if(!received) { + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForNotification wait"); + try { + wait(); + } catch(InterruptedException ie) { + // OK : we wait for being interrupted + } + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForNotification received"); + } + else + break; + } + String ret = type; + reset(); + return ret; + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/SimpleMBean.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/SimpleMBean.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 interface defines a simple standard MBean. + */ +@SqeDescriptorKey("INTERFACE SimpleMBean") +public interface SimpleMBean { + + @SqeDescriptorKey("ATTRIBUTE Attribute") + public String getAttribute(); + + @SqeDescriptorKey("ATTRIBUTE Attribute") + public void setAttribute(String s); + + @SqeDescriptorKey("ATTRIBUTE OperationInvoked") + public boolean getOperationInvoked(); + + @SqeDescriptorKey("ATTRIBUTE Operation2Invoked") + public boolean getOperation2Invoked(); + + // Void operation + // The associated MBeanOperationInfo is mapped to OpenMBeanOperationInfo + // => openType is added to the descriptor + @SqeDescriptorKey(value = "OPERATION operation", + descriptorFields = {"openType=SimpleType.VOID"}) + public void operation(); + + @SqeDescriptorKey("OPERATION operation2") + public String operation2(int i); + + // Void operation + // The associated MBeanOperationInfo is mapped to OpenMBeanOperationInfo + // => openType is added to the descriptor + @SqeDescriptorKey(value = "OPERATION reset", + descriptorFields = {"openType=SimpleType.VOID"}) + public void reset(); +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/SqeDescriptorKey.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/SqeDescriptorKey.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import javax.management.DescriptorKey; + +/** + * That annotation is usable everywhere DescriptorKey is (and even more). + * It is for use to test that you can retrieve the SqeDescriptorKey into the + * appropriate Descriptor instances as built by the JMX runtime. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface SqeDescriptorKey { + @DescriptorKey("sqeDescriptorKey") + String value(); + + // List descriptor fields that may be added or may be updated + // when retrieving an MBeanInfo using a JMXWS connection compared to the + // MBeanInfo returned by a local MBeanServer. + // The annotation format is : + // = + // The values actually handled by the test suite are : + // openType=SimpleType.VOID + @DescriptorKey("descriptorFields") + String[] descriptorFields() default {}; +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/TestJMXAuthenticator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/TestJMXAuthenticator.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Principal; + +import javax.management.Attribute; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.remote.JMXAuthenticator; +import javax.management.remote.JMXPrincipal; +import javax.security.auth.Subject; + +public final class TestJMXAuthenticator implements JMXAuthenticator { + + private String protocol = ""; + private MBeanServer mbs = null; + + public TestJMXAuthenticator() { + } + + public TestJMXAuthenticator(String protocol) { + this.protocol = protocol; + } + + public TestJMXAuthenticator(String protocol, MBeanServer mbs) { + this.protocol = protocol; + this.mbs = mbs; + } + + public Subject authenticate(Object credentials) { + + String credentials_username = ""; + String credentials_password = ""; + Principal aPrincipal = null; + + credentials_username = ((String[]) credentials)[0]; + credentials_password = ((String[]) credentials)[1]; + + String authenticated_username = System.getProperty("susername"); + String authenticated_password = System.getProperty("spassword"); + String principal = System.getProperty("principal"); + + System.out.println("TestJMXAuthenticator::authenticate: Start"); + System.out.println("TestJMXAuthenticator::authenticate: credentials username = " + + credentials_username); + System.out.println("TestJMXAuthenticator::authenticate: credentials password = " + + credentials_password); + System.out.println("TestJMXAuthenticator::authenticate: authenticated username = " + + authenticated_username); + System.out.println("TestJMXAuthenticator::authenticate: authenticated password = " + + authenticated_password); + System.out.println("TestJMXAuthenticator::authenticate: principal used for " + + "authorization = " + principal); + + if (credentials_username.equals(authenticated_username) && + credentials_password.equals(authenticated_password)) { + System.out.println("TestJMXAuthenticator::authenticate: " + + "Authenticator should succeed"); + } else { + System.out.println("TestJMXAuthenticator::authenticate: " + + "Authenticator should reject"); + throw new SecurityException("TestJMXAuthenticator throws EXCEPTION"); + } + + // At this point, authentication has succeeded + // (no SecurityException thrown). + // + // If no authorization is required, the returned subject (empty or not) + // is useless. + // Otherwise, the returned subject must define a principal + // and authorization will be performed against this principal. + // + // Note that this custom JMXAuthenticator is used for test purpose and + // the username used to perform authentication may be different from the + // username used to perform authorization. + // + Subject subject = new Subject(); + + if (principal != null) { + System.out.println("TestJMXAuthenticator::authenticate: " + + "Add " + principal + " principal to the returned subject"); + subject.getPrincipals().add(new JMXPrincipal(principal)); + } + + return subject; + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/TestSampleLoginModule.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/TestSampleLoginModule.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.Map; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + + +public final class TestSampleLoginModule implements LoginModule { + + private Subject subject; + private CallbackHandler callbackHandler; + private Map sharedState; + private Map options; + + public TestSampleLoginModule() { + } + + public void initialize(Subject subject, + CallbackHandler callbackHandler, + Map sharedState, + Map options) { + + this.subject = subject; + this.callbackHandler = callbackHandler; + this.sharedState = sharedState; + this.options = options; + } + + /* + * Authenticate the user by comparing the values of the java properties + * (username and password) against the values of the credentials. + * */ + public boolean login() throws LoginException { + + String credentials_username = null; + String credentials_password = null; + String authenticated_username = System.getProperty("susername"); + String authenticated_password = System.getProperty("spassword"); + + System.out.println("TestSampleLoginModule::login: Start"); + + // First retreive the credentials {username, password} from + // the callback handler + Callback[] callbacks = new Callback[2]; + callbacks[0] = new NameCallback("username"); + callbacks[1] = new PasswordCallback("password", false); + try { + callbackHandler.handle(callbacks); + credentials_username = ((NameCallback)callbacks[0]).getName(); + credentials_password = new String(((PasswordCallback)callbacks[1]). + getPassword()); + } catch (Exception e) { + throw new LoginException(e.toString()); + } + + System.out.println("TestSampleLoginModule::login: credentials username = " + + credentials_username); + System.out.println("TestSampleLoginModule::login: credentials password = " + + credentials_password); + System.out.println("TestSampleLoginModule::login: authenticated username = " + + authenticated_username); + System.out.println("TestSampleLoginModule::login: authenticated password = " + + authenticated_password); + + if (credentials_username.equals(authenticated_username) && + credentials_password.equals(authenticated_password)) { + System.out.println("TestSampleLoginModule::login: " + + "Authentication should succeed"); + return true; + } else { + System.out.println("TestSampleLoginModule::login: " + + "Authentication should reject"); + throw new LoginException("TestSampleLoginModule throws EXCEPTION"); + } + } + + public boolean commit() throws LoginException { + return true; + } + + public boolean abort() throws LoginException { + return true; + } + + public boolean logout() throws LoginException { + return true; + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/Utils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/Utils.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Map; +import java.util.HashMap; +import java.util.Properties; +import java.util.StringTokenizer; +import java.lang.reflect.Method; +import javax.management.remote.JMXConnectorServerMBean; + +// utility class for MXBean* tests coming from JMX Tonga test suite +class Utils { + + private static final String SERVER_SIDE_NAME = "-server"; + private static final String CLIENT_SIDE_NAME = "-client"; + + // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property + private static final String DEBUG_HEADER = "[debug] "; + + // DEBUG levels + private static int selectedDebugLevel = 0; + static final int DEBUG_STANDARD = 1; + static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests + static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE; + + static void parseDebugProperties() { + int level = 0; + Properties p = System.getProperties(); + + // get selected levels + if (p.getProperty("DEBUG_STANDARD") != null) { + level |= DEBUG_STANDARD; + } + + if (p.getProperty("DEBUG_VERBOSE") != null) { + level |= DEBUG_VERBOSE; + } + + if (p.getProperty("DEBUG_ALL") != null) { + level |= DEBUG_ALL; + } + + selectedDebugLevel = level; + } + + /** + * Reproduces the original parsing and collection of test parameters + * from the DTonga JMX test suite. + * + * Collects passed args and returns them in a map(argname, value) structure, + * which will be then propagated as necessary to various called methods. + */ + static Map parseParameters(String args[]) + throws Exception { + Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start"); + HashMap map = new HashMap<>(); + + for ( int i = 0; i < args.length; i++ ) { + if ( args[i].trim().startsWith("-") ) { + if ((i+1) < args.length && !args[i+1].startsWith("-") ) { + Utils.debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with value " + + args[i+1]) ; + map.put(args[i].trim(), args[i+1].trim()) ; + } else if ((i+1) < args.length && args[i+1].startsWith("-") || + (i+1) == args.length ) { + Utils.debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with null value") ; + map.put(args[i].trim(), null) ; + } else { + System.out.println( + "TestRoot::parseParameters: (WARNING) not added in map = " + + args[i]) ; + } + } + } + + Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ; + return map ; + } + + // Parse server parameters and put them in passed serverMap + static int parseServerParameters(String args[], + String serverSideName, + Map serverMap ) + throws Exception { + Utils.debug(Utils.DEBUG_STANDARD, + serverSideName + "::parseServerParameters: Start"); + + int nextIndex = 0; + boolean seenServerFlag = false; + + for ( int i = 0; i < args.length; i++ ) { + // Case of reaching "-server" flag parameter + if (args[i].equals(SERVER_SIDE_NAME)) { + if (!seenServerFlag) { + seenServerFlag = true; + continue; + } else { + // Already parsing server params, invalid params list + Utils.debug(Utils.DEBUG_STANDARD, + serverSideName + "::parseParameters: Invalid " + + args[i] + " parameter detected in " + + SERVER_SIDE_NAME + " parameters list"); + nextIndex = -1; + throw new RuntimeException("Invalid Parameter list"); + } + } + + // Case of reaching "-client" flag parameter + if (args[i].equals(CLIENT_SIDE_NAME)) { + // While parsing server parameters, then parsing is done. + Utils.debug(Utils.DEBUG_STANDARD, + serverSideName + "::parseServerParameters: Parsing of " + + SERVER_SIDE_NAME + " parameters done."); + return i; + } + + i = parseParamAtIndex(args, i, serverMap); + nextIndex = i; + } + + Utils.debug(Utils.DEBUG_STANDARD, + serverSideName + "::parseServerParameters: Parsing of parameters done"); + + return nextIndex; + } + + // Parse client parameters and put them in passed clientMap + static void parseClientParameters(String args[], + String clientSideName, + Map clientMap ) + throws Exception { + + Utils.debug(Utils.DEBUG_STANDARD, + clientSideName + "::parseClientParameters: Start"); + + boolean seenClientFlag = false; + + for ( int i = 0; i < args.length; i++ ) { + // Case of reaching "-client" flag parameter + if (args[i].equals(CLIENT_SIDE_NAME)) { + if (!seenClientFlag) { + seenClientFlag = true; + continue; + } else { + // Already parsing client params, invalid params list + Utils.debug(Utils.DEBUG_STANDARD, + clientSideName + "::parseClientParameters: Invalid " + + CLIENT_SIDE_NAME + " parameter detected in " + + CLIENT_SIDE_NAME + " parameters list."); + throw new RuntimeException("Invalid parameter in " + + clientSideName + " parameter list"); + } + } + + // Case of reaching "-server" flag parameter + if (args[i].equals(SERVER_SIDE_NAME)) { + // While parsing client parameters, invalid parameter list. + Utils.debug(Utils.DEBUG_STANDARD, + clientSideName + "::parseClientParameters: Invalid " + + SERVER_SIDE_NAME + " parameter inside " + + CLIENT_SIDE_NAME + " parameters list."); + throw new RuntimeException("Invalid parameter in " + + clientSideName + " parameter list"); + } + + i = parseParamAtIndex(args, i, clientMap); + } + + Utils.debug(Utils.DEBUG_STANDARD, + clientSideName + "::parseClientParameters: Parsing of parameters done."); + } + + // Add param found at index to passed map + // We only accept either "-param value" or "-param" form. + // The "value" form is invalid but just ignored. + private static int parseParamAtIndex(String args[], + int index, + Map map) { + + if (args[index].trim().startsWith("-") ) { + // Case of a "-param value" form + if ((index+1) < args.length && !args[index+1].startsWith("-") ) { + Utils.debug(Utils.DEBUG_STANDARD, + "TestRoot::parseParamAtIndex: added in map = " + + args[index] + + " with value " + + args[index+1]) ; + // adding ("param", value) to the passed map + map.put(args[index].trim(), args[index+1].trim()) ; + // value should not be parsed a second time + return index+1; + } + // Case of a "-param" form (flag parameter) + else if (((index+1) < args.length && args[index+1].startsWith("-")) || + (index+1) == args.length ) { + Utils.debug(Utils.DEBUG_STANDARD, + "TestRoot::parseParamAtIndex: added in map = " + + args[index] + + " with null value") ; + // adding ("param", null) to passed map + map.put(args[index].trim(), null) ; + } + } else { + // Unsupported "value" alone parameter + Utils.debug(Utils.DEBUG_STANDARD, + "TestRoot::parseParamAtIndex: invalid " + + " value-alone \"" + args[index] + "\" parameter." + + " Parameter ignored."); + } + + return index; + } + + /** + * This method is to be used in all tests to print anything + * that is temporary. + * Printing is done only when debug is activated by the property DEBUG. + * Printing depends also on the DEBUG_LEVEL property. + * Here it encapsulates a System.out.println. + */ + static void debug(int level, String line) { + if ((selectedDebugLevel & level) != 0) { + System.out.println(DEBUG_HEADER + line); + } + } + + /** + * Do print stack trace when withStack is true. + * Does try to call getTargetException() and getTargetError() then + * print embedded stacks in the case of an Exception wrapping + * another Exception or an Error. Recurse until no more wrapping + * is found. + */ + static void printThrowable(Throwable theThro, boolean withStack) { + try { + if (withStack) { + theThro.printStackTrace(System.out); + } + if (theThro instanceof Exception) { + Exception t = (Exception) theThro; + Method target = null; + String blank = " "; + try { + target = t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not + } + System.out.println(blank + t.getClass() + "==>" + t.getMessage()); + while (target != null) { + try { + t = (Exception) target.invoke(t, + (java.lang.Object[]) null); + } catch (Exception ee) { + t = null; + } + try { + if (t != null) { + blank = blank + " "; + System.out.println(blank + t.getClass() + "==>" + + t.getMessage()); + try { + target = + t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not } + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + + // We may have exceptions wrapping an Error then it is + // getTargetError that is likely to be called + try { + target = ((Exception) theThro).getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + Throwable err = theThro; + while (target != null) { + try { + err = (Error) target.invoke(err, + (java.lang.Object[]) null); + } catch (Exception ee) { + err = null; + } + try { + if (err != null) { + blank = blank + " "; + System.out.println(blank + err.getClass() + "==>" + + err.getMessage()); + if (withStack) { + err.printStackTrace(System.out); + } + try { + target = err.getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + } else { + System.out.println("Throwable is : " + theThro); + } + } catch (Throwable x) { + System.out.println("Exception : raised in printException : " + x); + } + } + + /** + * Wait up to maxTimeInSeconds second(s) the given JMX connector server + * comes up (which means isActive returns true). + * If it fails to do so we throw a RunTime exception. + */ + static void waitReady(JMXConnectorServerMBean server, + int maxTimeInSeconds) throws Exception { + int elapsed = 0; + + while (!server.isActive() && elapsed < maxTimeInSeconds) { + Thread.sleep(1000); + elapsed++; + } + + if (server.isActive()) { + String message = "Utils::waitReady: JMX connector server came up"; + if ( elapsed == 0) { + message += " immediately"; + } else { + message += " after " + elapsed + " seconds"; + } + message += " [" + server.getAddress() + "]"; + Utils.debug(DEBUG_STANDARD, message); + } else { + String message = "Utils::waitReady: (ERROR) JMX connector" + + " server didn't come up after " + elapsed + " seconds [" + + server.getAddress() + "]"; + System.out.println(message); + throw new RuntimeException(message); + } + } + + /** + * This method is used to compare the specified Throwable and possibly + * the derived causes to the specified String argument. + * The expected String argument format is : + * throwable_1;throwable_2;...;throwable_N + * where throwable_i can be : + * - either a throwable class name + * - or the "*" character meaning several unknown throwable class names + * This character must be followed by a throwable class name + */ + static boolean compareThrowable( + Throwable t, + String expectedThrowable) { + + // First parse the expectedThrowable String + StringTokenizer tokenizer = new StringTokenizer(expectedThrowable, ";"); + String token = null; + + try { + while (tokenizer.hasMoreTokens()) { + token = tokenizer.nextToken(); + if (!token.equals("*")) { + if (!Class.forName(token).isInstance(t)) { + return false; + } + } else { + token = tokenizer.nextToken(); + while (!Class.forName(token).isInstance(t)) { + t = t.getCause(); + if (t == null) { + return false; + } + } + } + t = t.getCause(); + } + } catch (ClassNotFoundException cnfe) { + String msg = "Expected throwable class(es) " + expectedThrowable + + " cannot be located"; + System.out.println(msg); + throw new IllegalArgumentException(msg); + } + return true; + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/access.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/access.properties Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,11 @@ +# Access control file for SQE tests. + +# Default username +SQE_username readwrite create Simple + +# Functional authorization tests +username1 readwrite create Simple +username2 readonly +username3 readonly +username4 readwrite create Simple +username5 readwrite create Simple diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/java.policy.authorization --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/java.policy.authorization Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,98 @@ +// Standard extensions get all permissions by default + +grant codeBase "file:${java.home}/lib/ext/*" { + permission java.security.AllPermission; +}; + +// default permissions granted to all domains +grant { + // Allows any thread to stop itself using the java.lang.Thread.stop() + // method that takes no argument. + // Note that this permission is granted by default only to remain + // backwards compatible. + // It is strongly recommended that you either remove this permission + // from this policy file or further restrict it to code sources + // that you specify, because Thread.stop() is potentially unsafe. + // See "http://java.sun.com/notes" for more information. + permission java.lang.RuntimePermission "stopThread"; + + // allows anyone to listen on un-privileged ports + permission java.net.SocketPermission "localhost:1024-", "listen"; + + // "standard" properies that can be read by anyone + + permission java.util.PropertyPermission "java.version", "read"; + permission java.util.PropertyPermission "java.vendor", "read"; + permission java.util.PropertyPermission "java.vendor.url", "read"; + permission java.util.PropertyPermission "java.class.version", "read"; + permission java.util.PropertyPermission "os.name", "read"; + permission java.util.PropertyPermission "os.version", "read"; + permission java.util.PropertyPermission "os.arch", "read"; + permission java.util.PropertyPermission "file.separator", "read"; + permission java.util.PropertyPermission "path.separator", "read"; + permission java.util.PropertyPermission "line.separator", "read"; + + permission java.util.PropertyPermission "java.specification.version", "read"; + permission java.util.PropertyPermission "java.specification.vendor", "read"; + permission java.util.PropertyPermission "java.specification.name", "read"; + + permission java.util.PropertyPermission "java.vm.specification.version", "read"; + permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; + permission java.util.PropertyPermission "java.vm.specification.name", "read"; + permission java.util.PropertyPermission "java.vm.version", "read"; + permission java.util.PropertyPermission "java.vm.vendor", "read"; + permission java.util.PropertyPermission "java.vm.name", "read"; + + permission java.io.FilePermission "*","read,write"; + +}; + +grant codeBase "file:/-" { + permission java.security.AllPermission; + permission java.io.FilePermission "*","read,write"; +}; + +grant principal javax.management.remote.JMXPrincipal "SQE_username" { + permission javax.management.MBeanServerPermission "*"; + permission javax.management.MBeanPermission "Simple", "instantiate"; + permission javax.management.MBeanPermission "Simple", "registerMBean"; +}; + +grant principal javax.management.remote.JMXPrincipal "username1" { + // + // JMXPrincipals "username1" has all permissions. + // + permission java.security.AllPermission; +}; + +grant principal javax.management.remote.JMXPrincipal "username2" { + // + // JMXPrincipals "username2" has all permissions. + // + permission java.security.AllPermission; +}; + +grant principal javax.management.remote.JMXPrincipal "username3" { + // + // JMXPrincipals "username3" has some permissions. + // + permission javax.management.MBeanPermission "Simple", "instantiate"; + permission javax.management.MBeanPermission "Simple", "registerMBean"; + permission javax.management.MBeanPermission "Simple", "setAttribute"; + permission javax.management.MBeanPermission "Simple", "invoke"; +}; + +grant principal javax.management.remote.JMXPrincipal "username4" { + // + // JMXPrincipals "username4" has all permissions. + // + permission javax.management.MBeanPermission "Simple", "instantiate"; + permission javax.management.MBeanPermission "Simple", "registerMBean"; + permission javax.management.MBeanPermission "Simple", "invoke"; +}; + +grant principal javax.management.remote.JMXPrincipal "username5" { + // + // JMXPrincipals "username5" has no permissions. + // +}; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/keystoreAgent Binary file jdk/test/javax/management/security/keystoreAgent has changed diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/keystoreClient Binary file jdk/test/javax/management/security/keystoreClient has changed diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/login.config --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/login.config Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,8 @@ +PasswordFileAuthentication { + com.sun.jmx.remote.security.FileLoginModule required + passwordFile="${password.file}"; +}; + +SampleLoginModule { + TestSampleLoginModule required; +}; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/password.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/management/security/password.properties Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,12 @@ +# Password file for default SQE username. +SQE_username SQE_password + +# Functional authorization tests +username1 password1 +username2 password2 +username3 password3 +username4 password4 +username5 password5 +username6 password6 + +usernameFileLoginModule passwordFileLoginModule diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/truststoreAgent Binary file jdk/test/javax/management/security/truststoreAgent has changed diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/management/security/truststoreClient Binary file jdk/test/javax/management/security/truststoreClient has changed diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/net/ssl/TLSv12/SignatureAlgorithms.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/net/ssl/TLSv12/SignatureAlgorithms.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + +/* + * @test + * @bug 8049321 + * @summary Support SHA256WithDSA in JSSE + * @run main/othervm SignatureAlgorithms PKIX "SHA-224,SHA-256" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA + * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-224" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA + * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-256" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA + * @run main/othervm SignatureAlgorithms PKIX "SHA-224,SHA-256" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 + * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-224" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 + * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-256" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 + */ + +import java.net.*; +import java.util.*; +import java.io.*; +import javax.net.ssl.*; +import java.security.Security; +import java.security.KeyStore; +import java.security.KeyFactory; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.*; +import java.security.interfaces.*; + +public class SignatureAlgorithms { + + /* + * ============================================================= + * Set the various variables needed for the tests, then + * specify what tests to run on each side. + */ + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + static boolean separateServerThread = true; + + /* + * Where do we find the keystores? + */ + // Certificates and key (DSA) used in the test. + static String trustedCertStr = + "-----BEGIN CERTIFICATE-----\n" + + "MIIDYTCCAyGgAwIBAgIJAK8/gw6zg/DPMAkGByqGSM44BAMwOzELMAkGA1UEBhMC\n" + + "VVMxDTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNl\n" + + "MB4XDTE1MTIwMzEzNTIyNVoXDTM2MTExMjEzNTIyNVowOzELMAkGA1UEBhMCVVMx\n" + + "DTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNlMIIB\n" + + "uDCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG4X+uxu5V\n" + + "b3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2eBfUv/hJ\n" + + "cLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNAqAB9PO5Y\n" + + "zKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAaor8iURd82\n" + + "b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I81drnN0Y\n" + + "lyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVXW+SEGADC\n" + + "J1+z8uqP3lIB6ltdgOiV/GQDgYUAAoGBAOXRppuJSGdt6AiZkb81P1DCUgIUlZFI\n" + + "J9GxWrjbbHDmGllMwPNhK6dU7LJKJJuYVPW+95rUGlSJEjRqSlHuyHkNb6e3e7qx\n" + + "tmx1/oIyq+oLult50hBS7uBvLLR0JbIKjBzzkudL8Rjze4G/Wq7KDM2T1JOP49tW\n" + + "eocCvaC8h8uQo4GtMIGqMB0GA1UdDgQWBBT17HcqLllsqnZzP+kElcGcBGmubjBr\n" + + "BgNVHSMEZDBigBT17HcqLllsqnZzP+kElcGcBGmubqE/pD0wOzELMAkGA1UEBhMC\n" + + "VVMxDTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNl\n" + + "ggkArz+DDrOD8M8wDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwCQYHKoZI\n" + + "zjgEAwMvADAsAhQ6Y1I6LtIEBMqNo8o6GIe4LLEJuwIUbVQUKi8tvtWyRoxm8AFV\n" + + "0axJYUU=\n" + + "-----END CERTIFICATE-----"; + + static String[] targetCertStr = { + // DSA-SHA1 + "-----BEGIN CERTIFICATE-----\n" + + "MIIDKTCCAumgAwIBAgIJAOy5c0b+8stFMAkGByqGSM44BAMwOzELMAkGA1UEBhMC\n" + + "VVMxDTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNl\n" + + "MB4XDTE1MTIwMzEzNTIyNVoXDTM1MDgyMDEzNTIyNVowTzELMAkGA1UEBhMCVVMx\n" + + "DTALBgNVBAoMBEphdmExHTAbBgNVBAsMFFN1bkpTU0UgVGVzdCBTZXJpdmNlMRIw\n" + + "EAYDVQQDDAlsb2NhbGhvc3QwggG3MIIBLAYHKoZIzjgEATCCAR8CgYEA8f5v4ZIx\n" + + "fopLuNcNF6/NzjrgMUbhf67G7lVvdTjKGxTwDxy4fiDCKfov8gOSZlDs3TMLSNGy\n" + + "IZVIywURNOrXQXf2kfZ4F9S/+Elwt8GciMES1WpX90TErzwe6XaxniZCKFDiKyw+\n" + + "XuGyuhL0RZiJydfSg0CoAH087ljMpt+kvtMCFQDXdZbjyuWfZQ/8toCC2fMqGpw2\n" + + "0wKBgQDdG/QaKtNsBqivyJRF3zZvkWUNN684JO6rtkil9lVXfSd5LtwFg163qb/f\n" + + "/hx9SVtuuqFuognYj4jzV2uc3RiXI3gqS7ERwHo5PB9aQhSPqu89oJCsEfxDbXds\n" + + "Orcce1g1o/w6h5BTJVdb5IQYAMInX7Py6o/eUgHqW12A6JX8ZAOBhAACgYB+zYqn\n" + + "jJwG4GZpBIN/6qhzbp0flChsV+Trlu0SL0agAQzb6XdI/4JnO87Pgbxaxh3VNAj3\n" + + "3+Ghr1NLBuBfTKzJ4j9msWT3EpLupkMyNtXvBYM0iyMrll67lSjMdv++wLEw35Af\n" + + "/bzVcjGyA5Q0i0cuEzDmHTVfi0OydynbwSLxtKNjMGEwCwYDVR0PBAQDAgPoMB0G\n" + + "A1UdDgQWBBQXJI8AxM0qsYCbbkIMuI5zJ+nMEDAfBgNVHSMEGDAWgBT17HcqLlls\n" + + "qnZzP+kElcGcBGmubjASBgNVHREBAf8ECDAGhwR/AAABMAkGByqGSM44BAMDLwAw\n" + + "LAIUXgyJ0xll4FrZAKXi8bj7Kiz+SA4CFH9WCSZIBYA9lmJkiTgRS7iM/6IC\n" + + "-----END CERTIFICATE-----", + + // DSA-SHA224 + "-----BEGIN CERTIFICATE-----\n" + + "MIIDLzCCAuugAwIBAgIJAOy5c0b+8stGMAsGCWCGSAFlAwQDATA7MQswCQYDVQQG\n" + + "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" + + "Y2UwHhcNMTUxMjAzMTU0NDM5WhcNMzUwODIwMTU0NDM5WjBPMQswCQYDVQQGEwJV\n" + + "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Ux\n" + + "EjAQBgNVBAMMCWxvY2FsaG9zdDCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQDx/m/h\n" + + "kjF+iku41w0Xr83OOuAxRuF/rsbuVW91OMobFPAPHLh+IMIp+i/yA5JmUOzdMwtI\n" + + "0bIhlUjLBRE06tdBd/aR9ngX1L/4SXC3wZyIwRLValf3RMSvPB7pdrGeJkIoUOIr\n" + + "LD5e4bK6EvRFmInJ19KDQKgAfTzuWMym36S+0wIVANd1luPK5Z9lD/y2gILZ8yoa\n" + + "nDbTAoGBAN0b9Boq02wGqK/IlEXfNm+RZQ03rzgk7qu2SKX2VVd9J3ku3AWDXrep\n" + + "v9/+HH1JW266oW6iCdiPiPNXa5zdGJcjeCpLsRHAejk8H1pCFI+q7z2gkKwR/ENt\n" + + "d2w6txx7WDWj/DqHkFMlV1vkhBgAwidfs/Lqj95SAepbXYDolfxkA4GEAAKBgA81\n" + + "CJKEv+pwiqYgxtw/9rkQ9748WP3mKrEC06kjUG+94/Z9dQloNFFfj6LiO1bymc5l\n" + + "6QIR8XCi4Po3N80K3+WxhBGFhY+RkVWTh43JV8epb41aH2qiWErarBwBGEh8LyGT\n" + + "i30db+Nkz2gfvyz9H/9T0jmYgfLEOlMCusali1qHo2MwYTALBgNVHQ8EBAMCA+gw\n" + + "HQYDVR0OBBYEFBqSP0S4+X+zOCTEnlp2hbAjV/W5MB8GA1UdIwQYMBaAFPXsdyou\n" + + "WWyqdnM/6QSVwZwEaa5uMBIGA1UdEQEB/wQIMAaHBH8AAAEwCwYJYIZIAWUDBAMB\n" + + "AzEAMC4CFQChiRaOnAnsCSJFwdpK22jSxU/mhQIVALgLbj/G39+1Ej8UuSWnEQyU\n" + + "4DA+\n" + + "-----END CERTIFICATE-----", + + // DSA-SHA256 + "-----BEGIN CERTIFICATE-----\n" + + "MIIDLTCCAuugAwIBAgIJAOy5c0b+8stHMAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" + + "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" + + "Y2UwHhcNMTUxMjAzMTU0NjUxWhcNMzUwODIwMTU0NjUxWjBPMQswCQYDVQQGEwJV\n" + + "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Ux\n" + + "EjAQBgNVBAMMCWxvY2FsaG9zdDCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQDx/m/h\n" + + "kjF+iku41w0Xr83OOuAxRuF/rsbuVW91OMobFPAPHLh+IMIp+i/yA5JmUOzdMwtI\n" + + "0bIhlUjLBRE06tdBd/aR9ngX1L/4SXC3wZyIwRLValf3RMSvPB7pdrGeJkIoUOIr\n" + + "LD5e4bK6EvRFmInJ19KDQKgAfTzuWMym36S+0wIVANd1luPK5Z9lD/y2gILZ8yoa\n" + + "nDbTAoGBAN0b9Boq02wGqK/IlEXfNm+RZQ03rzgk7qu2SKX2VVd9J3ku3AWDXrep\n" + + "v9/+HH1JW266oW6iCdiPiPNXa5zdGJcjeCpLsRHAejk8H1pCFI+q7z2gkKwR/ENt\n" + + "d2w6txx7WDWj/DqHkFMlV1vkhBgAwidfs/Lqj95SAepbXYDolfxkA4GEAAKBgEF7\n" + + "2qiYxGrjX4KCOy0k5nK/RYlgLy4gYDChihQpiaa+fbA5JOBOxPWsh7rdtmJuDrEJ\n" + + "keacU223+DIhOKC49fa+EvhLNqo6U1oPn8n/yvBsvvnWkcynw5KfNzaLlaPmzugh\n" + + "v9xl/GhyZNAXc1QUcW3C+ceHVNrKnkfbTKZz5eRSo2MwYTALBgNVHQ8EBAMCA+gw\n" + + "HQYDVR0OBBYEFNMkPrt40oO9Dpy+bcbQdEvOlNlyMB8GA1UdIwQYMBaAFPXsdyou\n" + + "WWyqdnM/6QSVwZwEaa5uMBIGA1UdEQEB/wQIMAaHBH8AAAEwCwYJYIZIAWUDBAMC\n" + + "Ay8AMCwCFCvA2QiKSe/n+6GqSYQwgQ/zL5M9AhQfSiuWdMJKWpgPJKakvzhBUbMb\n" + + "vA==\n" + + "-----END CERTIFICATE-----"}; + + // Private key in the format of PKCS#8, key size is 1024 bits. + static String[] targetPrivateKey = { + // For cert DSA-SHA1 + "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG\n" + + "4X+uxu5Vb3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2\n" + + "eBfUv/hJcLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNA\n" + + "qAB9PO5YzKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAao\n" + + "r8iURd82b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I\n" + + "81drnN0YlyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVX\n" + + "W+SEGADCJ1+z8uqP3lIB6ltdgOiV/GQEFgIUOiB7J/lrFrNduQ8nDNTe8VspoAI=", + + // For cert DSA-SHA224 + "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG\n" + + "4X+uxu5Vb3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2\n" + + "eBfUv/hJcLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNA\n" + + "qAB9PO5YzKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAao\n" + + "r8iURd82b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I\n" + + "81drnN0YlyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVX\n" + + "W+SEGADCJ1+z8uqP3lIB6ltdgOiV/GQEFgIUOj9F5mxWd9W1tiLSdsOAt8BUBzE=", + + // For cert DSA-SHA256 + "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG\n" + + "4X+uxu5Vb3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2\n" + + "eBfUv/hJcLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNA\n" + + "qAB9PO5YzKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAao\n" + + "r8iURd82b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I\n" + + "81drnN0YlyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVX\n" + + "W+SEGADCJ1+z8uqP3lIB6ltdgOiV/GQEFgIUQ2WGgg+OO39Aujj0e4lM4pP4/9g="}; + + + static char passphrase[] = "passphrase".toCharArray(); + + /* + * Turn on SSL debugging? + */ + static boolean debug = false; + + /* + * Is the server ready to serve? + */ + volatile boolean serverReady = false; + + /* + * Define the server side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doServerSide() throws Exception { + + SSLContext context = generateSSLContext( + null, targetCertStr, targetPrivateKey); + SSLServerSocketFactory sslssf = context.getServerSocketFactory(); + try (SSLServerSocket sslServerSocket = + (SSLServerSocket)sslssf.createServerSocket(serverPort)) { + + serverPort = sslServerSocket.getLocalPort(); + + /* + * Signal Client, we're ready for his connect. + */ + serverReady = true; + + try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) { + sslSocket.setEnabledCipherSuites( + sslSocket.getSupportedCipherSuites()); + InputStream sslIS = sslSocket.getInputStream(); + OutputStream sslOS = sslSocket.getOutputStream(); + + sslIS.read(); + sslOS.write('A'); + sslOS.flush(); + + dumpSignatureAlgorithms(sslSocket); + } + } + } + + /* + * Define the client side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doClientSide() throws Exception { + + /* + * Wait for server to get started. + */ + while (!serverReady) { + Thread.sleep(50); + } + + SSLContext context = generateSSLContext(trustedCertStr, null, null); + SSLSocketFactory sslsf = context.getSocketFactory(); + + try (SSLSocket sslSocket = + (SSLSocket)sslsf.createSocket("localhost", serverPort)) { + + // enable TLSv1.2 only + sslSocket.setEnabledProtocols(new String[] {"TLSv1.2"}); + + // enable a block cipher + sslSocket.setEnabledCipherSuites(new String[] {cipherSuite}); + + InputStream sslIS = sslSocket.getInputStream(); + OutputStream sslOS = sslSocket.getOutputStream(); + + sslOS.write('B'); + sslOS.flush(); + sslIS.read(); + + dumpSignatureAlgorithms(sslSocket); + } + } + + static void dumpSignatureAlgorithms(SSLSocket sslSocket) throws Exception { + + boolean isClient = sslSocket.getUseClientMode(); + String mode = "[" + (isClient ? "Client" : "Server") + "]"; + ExtendedSSLSession session = + (ExtendedSSLSession)sslSocket.getSession(); + String[] signAlgs = session.getLocalSupportedSignatureAlgorithms(); + System.out.println( + mode + " local supported signature algorithms: " + + Arrays.asList(signAlgs)); + + if (!isClient) { + signAlgs = session.getPeerSupportedSignatureAlgorithms(); + System.out.println( + mode + " peer supported signature algorithms: " + + Arrays.asList(signAlgs)); + } else { + Certificate[] serverCerts = session.getPeerCertificates(); + + // server should always send the authentication cert. + String sigAlg = ((X509Certificate)serverCerts[0]).getSigAlgName(); + System.out.println( + mode + " the signature algorithm of server certificate: " + + sigAlg); + if (sigAlg.contains("SHA1")) { + if (disabledAlgorithms.contains("SHA-1")) { + throw new Exception( + "Not the expected server certificate. " + + "SHA-1 should be disabled"); + } + } else if (sigAlg.contains("SHA224")) { + if (disabledAlgorithms.contains("SHA-224")) { + throw new Exception( + "Not the expected server certificate. " + + "SHA-224 should be disabled"); + } + } else { // SHA-256 + if (disabledAlgorithms.contains("SHA-256")) { + throw new Exception( + "Not the expected server certificate. " + + "SHA-256 should be disabled"); + } + } + } + } + + /* + * ============================================================= + * The remainder is just support stuff + */ + private static String tmAlgorithm; // trust manager + private static String disabledAlgorithms; // disabled algorithms + private static String cipherSuite; // cipher suite + + private static void parseArguments(String[] args) { + tmAlgorithm = args[0]; + disabledAlgorithms = args[1]; + cipherSuite = args[2]; + } + + private static SSLContext generateSSLContext(String trustedCertStr, + String[] keyCertStrs, String[] keySpecStrs) throws Exception { + + // generate certificate from cert string + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // create a key store + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + + // import the trused cert + Certificate trusedCert = null; + ByteArrayInputStream is = null; + if (trustedCertStr != null) { + is = new ByteArrayInputStream(trustedCertStr.getBytes()); + trusedCert = cf.generateCertificate(is); + is.close(); + + ks.setCertificateEntry("DSA Signer", trusedCert); + } + + if (keyCertStrs != null && keyCertStrs.length != 0) { + for (int i = 0; i < keyCertStrs.length; i++) { + String keyCertStr = keyCertStrs[i]; + String keySpecStr = keySpecStrs[i]; + + // generate the private key. + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + Base64.getMimeDecoder().decode(keySpecStr)); + KeyFactory kf = KeyFactory.getInstance("DSA"); + DSAPrivateKey priKey = + (DSAPrivateKey)kf.generatePrivate(priKeySpec); + + // generate certificate chain + is = new ByteArrayInputStream(keyCertStr.getBytes()); + Certificate keyCert = cf.generateCertificate(is); + is.close(); + + Certificate[] chain = null; + if (trusedCert != null) { + chain = new Certificate[2]; + chain[0] = keyCert; + chain[1] = trusedCert; + } else { + chain = new Certificate[1]; + chain[0] = keyCert; + } + + // import the key entry. + ks.setKeyEntry("DSA Entry " + i, priKey, passphrase, chain); + } + } + + // create SSL context + TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm); + tmf.init(ks); + + SSLContext ctx = SSLContext.getInstance("TLS"); + if (keyCertStrs != null && keyCertStrs.length != 0) { + KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509"); + kmf.init(ks, passphrase); + + ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + ks = null; + } else { + ctx.init(null, tmf.getTrustManagers(), null); + } + + return ctx; + } + + + // use any free port by default + volatile int serverPort = 0; + + volatile Exception serverException = null; + volatile Exception clientException = null; + + public static void main(String[] args) throws Exception { + /* + * debug option + */ + if (debug) { + System.setProperty("javax.net.debug", "all"); + } + + /* + * Get the customized arguments. + */ + parseArguments(args); + + + /* + * Ignore testing on Windows if only SHA-224 is available. + */ + if ((Security.getProvider("SunMSCAPI") != null) && + (disabledAlgorithms.contains("SHA-1")) && + (disabledAlgorithms.contains("SHA-256"))) { + + System.out.println( + "Windows system does not support SHA-224 algorithms yet. " + + "Ignore the testing"); + + return; + } + + /* + * Expose the target algorithms by diabling unexpected algorithms. + */ + Security.setProperty( + "jdk.certpath.disabledAlgorithms", disabledAlgorithms); + + /* + * Reset the security property to make sure that the algorithms + * and keys used in this test are not disabled by default. + */ + Security.setProperty( "jdk.tls.disabledAlgorithms", ""); + + /* + * Start the tests. + */ + new SignatureAlgorithms(); + } + + Thread clientThread = null; + Thread serverThread = null; + + /* + * Primary constructor, used to drive remainder of the test. + * + * Fork off the other side, then do your work. + */ + SignatureAlgorithms() throws Exception { + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + // swallow for now. Show later + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + serverThread.join(); + } else { + clientThread.join(); + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + String whichRemote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + whichRemote = "server"; + } else { + remote = clientException; + local = serverException; + whichRemote = "client"; + } + + /* + * If both failed, return the curthread's exception, but also + * print the remote side Exception + */ + if ((local != null) && (remote != null)) { + System.out.println(whichRemote + " also threw:"); + remote.printStackTrace(); + System.out.println(); + throw local; + } + + if (remote != null) { + throw remote; + } + + if (local != null) { + throw local; + } + } + + void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died..." + e); + serverReady = true; + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + serverException = e; + } finally { + serverReady = true; + } + } + } + + void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..." + e); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + clientException = e; + } + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/print/attribute/Services_getDocFl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/print/attribute/Services_getDocFl.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 javax.print.DocFlavor; +import javax.print.PrintService; +import javax.print.PrintServiceLookup; +import javax.print.attribute.HashPrintRequestAttributeSet; + +/* + * @test + * @bug 4901243 8040139 + * @summary JPG, GIF, and PNG DocFlavors (URL) should be supported if Postscript is supported. + * @run main Services_getDocFl +*/ + + +public class Services_getDocFl { + public static void main (String [] args) { + + HashPrintRequestAttributeSet prSet = null; + boolean psSupported = false, + pngImagesSupported = false, + gifImagesSupported = false, + jpgImagesSupported = false; + String mimeType; + + PrintService[] serv = PrintServiceLookup.lookupPrintServices(null, null); + if (serv.length==0) { + System.out.println("no PrintService found"); + } else { + System.out.println("number of Services "+serv.length); + } + + + for (int i = 0; i errors = new HashSet<>(); - for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) { - try { - if (mdp.isDeviceSupported(fake)) { - throw new RuntimeException("fake is supported"); - } - final MidiDevice device = mdp.getDevice(fake); - System.err.println("MidiDevice: " + device); - throw new RuntimeException("IllegalArgumentException expected"); - } catch (final IllegalArgumentException e) { - errors.add(e.getMessage()); - } - } - if (errors.size() != 1) { - throw new RuntimeException("Wrong number of messages:" + errors); - } - } -} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/sound/midi/MidiDeviceProvider/NullInfo.java --- a/jdk/test/javax/sound/midi/MidiDeviceProvider/NullInfo.java Wed Dec 23 15:41:50 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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.Collection; -import java.util.HashSet; - -import javax.sound.midi.MidiDevice; -import javax.sound.midi.MidiSystem; -import javax.sound.midi.MidiUnavailableException; -import javax.sound.midi.spi.MidiDeviceProvider; - -import static java.util.ServiceLoader.load; - -/** - * @test - * @bug 8058115 - * @summary MidiDeviceProvider shouldn't returns incorrect results or throw NPE - * in case of null MidiDevice.Info - * @author Sergey Bylokhov - */ -public final class NullInfo { - - public static void main(final String[] args) { - // MidiSystem API - try { - MidiSystem.getMidiDevice(null); - throw new RuntimeException("IllegalArgumentException expected"); - } catch (final MidiUnavailableException e) { - throw new RuntimeException("IllegalArgumentException expected", e); - } catch (final IllegalArgumentException ignored) { - // expected - } - // MidiDeviceProvider API - final Collection errors = new HashSet<>(); - for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) { - try { - if (mdp.isDeviceSupported(null)) { - throw new RuntimeException("null is supported"); - } - final MidiDevice device = mdp.getDevice(null); - System.err.println("MidiDevice: " + device); - throw new RuntimeException("IllegalArgumentException expected"); - } catch (final IllegalArgumentException e) { - errors.add(e.getMessage()); - } - } - if (errors.size() != 1) { - throw new RuntimeException("Wrong number of messages:" + errors); - } - } -} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/sound/midi/MidiDeviceProvider/UnsupportedInfo.java --- a/jdk/test/javax/sound/midi/MidiDeviceProvider/UnsupportedInfo.java Wed Dec 23 15:41:50 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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 javax.sound.midi.MidiDevice; -import javax.sound.midi.MidiSystem; -import javax.sound.midi.spi.MidiDeviceProvider; - -import static java.util.ServiceLoader.load; - -/** - * @test - * @bug 8058115 - * @summary MidiDeviceProvider shouldn't returns incorrect results in case of - * unsupported MidiDevice.Info - * @author Sergey Bylokhov - */ -public final class UnsupportedInfo { - - public static void main(final String[] args) { - final MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo(); - for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) { - for (final MidiDevice.Info info : infos) { - if (mdp.isDeviceSupported(info)) { - if (mdp.getDevice(info) == null) { - throw new RuntimeException("MidiDevice is null"); - } - } else { - try { - mdp.getDevice(info); - throw new RuntimeException( - "IllegalArgumentException expected"); - } catch (final IllegalArgumentException ignored) { - // expected - } - } - } - } - } -} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Objects; + +import javax.sound.midi.MidiDevice; +import javax.sound.midi.MidiSystem; +import javax.sound.midi.spi.MidiDeviceProvider; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8143909 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + public static void main(final String[] args) throws Exception { + testMS(); + for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) { + testMDP(mdp); + } + testMDP(customMDP); + } + + /** + * Tests the part of MidiSystem API, which implemented via + * MidiDeviceProvider. + */ + private static void testMS() throws Exception { + // MidiSystem#getMidiDevice(MidiDevice.Info) + try { + MidiSystem.getMidiDevice(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + /** + * Tests the MidiDeviceProvider API directly. + */ + private static void testMDP(final MidiDeviceProvider mdp) throws Exception { + // MidiDeviceProvider#isDeviceSupported(Info) + try { + mdp.isDeviceSupported(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiDeviceProvider#getDevice(Info) + try { + mdp.getDevice(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + /** + * Tests some default implementation of MidiDeviceProvider API, using the + * custom {@code MidiDeviceProvider}, which support nothing. + */ + static MidiDeviceProvider customMDP = new MidiDeviceProvider() { + @Override + public MidiDevice.Info[] getDeviceInfo() { + return new MidiDevice.Info[0]; + } + + @Override + public MidiDevice getDevice(MidiDevice.Info info) { + Objects.requireNonNull(info); + return null; + } + }; +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/sound/midi/spi/MidiDeviceProvider/FakeInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/FakeInfo.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.Collection; +import java.util.HashSet; + +import javax.sound.midi.MidiDevice; +import javax.sound.midi.MidiDevice.Info; +import javax.sound.midi.MidiSystem; +import javax.sound.midi.MidiUnavailableException; +import javax.sound.midi.spi.MidiDeviceProvider; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8059743 + * @summary MidiDeviceProvider shouldn't returns incorrect results in case of + * some unknown MidiDevice.Info + * @author Sergey Bylokhov + */ +public final class FakeInfo { + + private static final class Fake extends Info { + + Fake() { + super("a", "b", "c", "d"); + } + } + + public static void main(final String[] args) { + final Info fake = new Fake(); + // MidiSystem API + try { + MidiSystem.getMidiDevice(fake); + throw new RuntimeException("IllegalArgumentException expected"); + } catch (final MidiUnavailableException e) { + throw new RuntimeException("IllegalArgumentException expected", e); + } catch (final IllegalArgumentException ignored) { + // expected + } + // MidiDeviceProvider API + final Collection errors = new HashSet<>(); + for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) { + try { + if (mdp.isDeviceSupported(fake)) { + throw new RuntimeException("fake is supported"); + } + final MidiDevice device = mdp.getDevice(fake); + System.err.println("MidiDevice: " + device); + throw new RuntimeException("IllegalArgumentException expected"); + } catch (final IllegalArgumentException e) { + errors.add(e.getMessage()); + } + } + if (errors.size() != 1) { + throw new RuntimeException("Wrong number of messages:" + errors); + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/sound/midi/spi/MidiDeviceProvider/UnsupportedInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/UnsupportedInfo.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 javax.sound.midi.MidiDevice; +import javax.sound.midi.MidiSystem; +import javax.sound.midi.spi.MidiDeviceProvider; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8058115 + * @summary MidiDeviceProvider shouldn't returns incorrect results in case of + * unsupported MidiDevice.Info + * @author Sergey Bylokhov + */ +public final class UnsupportedInfo { + + public static void main(final String[] args) { + final MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo(); + for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) { + for (final MidiDevice.Info info : infos) { + if (mdp.isDeviceSupported(info)) { + if (mdp.getDevice(info) == null) { + throw new RuntimeException("MidiDevice is null"); + } + } else { + try { + mdp.getDevice(info); + throw new RuntimeException( + "IllegalArgumentException expected"); + } catch (final IllegalArgumentException ignored) { + // expected + } + } + } + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/sound/midi/spi/MidiFileReader/ExpectedNPEOnNull.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/sound/midi/spi/MidiFileReader/ExpectedNPEOnNull.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.File; +import java.io.InputStream; +import java.net.URL; + +import javax.sound.midi.MidiSystem; +import javax.sound.midi.spi.MidiFileReader; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8143909 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + public static void main(final String[] args) throws Exception { + testMS(); + for (final MidiFileReader mfr : load(MidiFileReader.class)) { + testMFR(mfr); + } + } + + /** + * Tests the part of MidiSystem API, which implemented via MidiFileReader. + */ + private static void testMS() throws Exception { + // MidiSystem#getMidiFileFormat(InputStream) + try { + MidiSystem.getMidiFileFormat((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getMidiFileFormat(URL) + try { + MidiSystem.getMidiFileFormat((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getMidiFileFormat(File) + try { + MidiSystem.getMidiFileFormat((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getSequence(InputStream) + try { + MidiSystem.getSequence((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getSequence(URL) + try { + MidiSystem.getSequence((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getSequence(File) + try { + MidiSystem.getSequence((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + /** + * Tests the MidiFileReader API directly. + */ + private static void testMFR(final MidiFileReader mfr) throws Exception { + // MidiFileReader#getMidiFileFormat(InputStream) + try { + mfr.getMidiFileFormat((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileReader#getMidiFileFormat(URL) + try { + mfr.getMidiFileFormat((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileReader#getMidiFileFormat(File) + try { + mfr.getMidiFileFormat((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileReader#getSequence(InputStream) + try { + mfr.getSequence((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileReader#getSequence(URL) + try { + mfr.getSequence((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileReader#getSequence(File) + try { + mfr.getSequence((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/sound/midi/spi/MidiFileWriter/ExpectedNPEOnNull.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/sound/midi/spi/MidiFileWriter/ExpectedNPEOnNull.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.File; +import java.io.OutputStream; +import java.util.Objects; + +import javax.sound.midi.MidiSystem; +import javax.sound.midi.Sequence; +import javax.sound.midi.spi.MidiFileWriter; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8143909 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + public static void main(final String[] args) throws Exception { + testMS(); + for (final MidiFileWriter mfw : load(MidiFileWriter.class)) { + testMFW(mfw); + } + testMFW(customMFW); + } + + /** + * Tests the part of MidiSystem API, which implemented via MidiFileWriter. + */ + private static void testMS() throws Exception { + // MidiSystem#getMidiFileTypes(Sequence) + try { + MidiSystem.getMidiFileTypes(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + + // MidiSystem#isFileTypeSupported(int, Sequence) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.isFileTypeSupported(type, null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + // MidiSystem#write(Sequence, int, OutputStream) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(null, type, new NullOutputStream()); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(new Sequence(0, 0), type, (OutputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(null, type, (OutputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + // MidiSystem#write(Sequence, int, File) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(null, type, new File("")); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(new Sequence(0, 0), type, (File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(null, type, (File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + } + + /** + * Tests the MidiFileWriter API directly. + */ + private static void testMFW(final MidiFileWriter mfw) throws Exception { + // MidiFileWriter#getMidiFileTypes(Sequence) + try { + mfw.getMidiFileTypes(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileWriter#isFileTypeSupported(int, Sequence) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.isFileTypeSupported(type, null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + // MidiFileWriter#write(Sequence, int, OutputStream) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(null, type, new NullOutputStream()); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(new Sequence(0, 0), type, (OutputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(null, type, (OutputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + // MidiFileWriter#write(Sequence, int, File) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(null, type, new File("")); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(new Sequence(0, 0), type, (File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(null, type, (File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + } + /** + * Tests some default implementation of MidiFileWriter API, using the custom + * {@code MidiFileWriter}, which support nothing. + */ + static MidiFileWriter customMFW = new MidiFileWriter() { + @Override + public int[] getMidiFileTypes() { + return new int[0]; + } + + @Override + public int[] getMidiFileTypes(Sequence sequence) { + Objects.requireNonNull(sequence); + return new int[0]; + } + + @Override + public int write(Sequence in, int fileType, OutputStream out) { + Objects.requireNonNull(in); + Objects.requireNonNull(out); + return 0; + } + + @Override + public int write(Sequence in, int fileType, File out) { + Objects.requireNonNull(in); + Objects.requireNonNull(out); + return 0; + } + }; + + private static final class NullOutputStream extends OutputStream { + + @Override + public void write(final int b) { + //do nothing + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/sound/midi/spi/SoundbankReader/ExpectedNPEOnNull.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/sound/midi/spi/SoundbankReader/ExpectedNPEOnNull.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.File; +import java.io.InputStream; +import java.net.URL; + +import javax.sound.midi.MidiSystem; +import javax.sound.midi.spi.SoundbankReader; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8143909 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + public static void main(final String[] args) throws Exception { + testMS(); + for (final SoundbankReader sbr : load(SoundbankReader.class)) { + testSBR(sbr); + } + } + + /** + * Tests the part of MidiSystem API, which implemented via SoundbankReader. + */ + private static void testMS() throws Exception { + // MidiSystem#getSoundbank(InputStream) + try { + MidiSystem.getSoundbank((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getSoundbank(URL) + try { + MidiSystem.getSoundbank((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getSoundbank(File) + try { + MidiSystem.getSoundbank((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + /** + * Tests the SoundbankReader API directly. + */ + private static void testSBR(final SoundbankReader sbr) throws Exception { + // SoundbankReader#getSoundbank(InputStream) + try { + sbr.getSoundbank((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // SoundbankReader#getSoundbank(URL) + try { + sbr.getSoundbank((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // SoundbankReader#getSoundbank(File) + try { + sbr.getSoundbank((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/swing/JFileChooser/8067660/FileChooserTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/swing/JFileChooser/8067660/FileChooserTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8067660 + * @summary JFileChooser create new folder fails silently + * @requires (os.family == "windows") + * @run main/manual FileChooserTest + */ +import java.awt.Panel; +import java.awt.TextArea; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class FileChooserTest { + + private static boolean theTestPassed; + private static boolean testGeneratedInterrupt; + private static Thread mainThread; + private static int sleepTime = 30000; + public static JFileChooser fileChooser; + + private static void init() throws Exception { + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + String[] instructions + = { + "1) Create a folder with read only permissions", + "2) Click on run test button.It will open a open dialog" + + " Navigate to the newly created read only folder", + "3) Click on the create new folder button in open dialog", + "4) If an error message does not pops up" + + "test failed otherwise passed.", + "5) Pressing Pass/Fail button will mark test as " + + "pass/fail and will shutdown JVM"}; + + Sysout.createDialogWithInstructions(instructions); + Sysout.printInstructions(instructions); + } + }); + } + + /** + * *************************************************** + * Standard Test Machinery Section DO NOT modify anything in this section -- + * it's a standard chunk of code which has all of the synchronisation + * necessary for the test harness. By keeping it the same in all tests, it + * is easier to read and understand someone else's test, as well as insuring + * that all tests behave correctly with the test harness. There is a section + * following this for test-defined classes + */ + public static void main(String args[]) throws Exception { + + mainThread = Thread.currentThread(); + try { + init(); + } catch (Exception ex) { + return; + } + try { + mainThread.sleep(sleepTime); + } catch (InterruptedException ex) { + Sysout.dispose(); + if (!theTestPassed && testGeneratedInterrupt) { + throw new RuntimeException("Test Failed"); + } + } + if (!testGeneratedInterrupt) { + Sysout.dispose(); + throw new RuntimeException("Test Failed"); + } + } + + public static synchronized void pass() { + theTestPassed = true; + testGeneratedInterrupt = true; + mainThread.interrupt(); + } + + public static synchronized void fail() { + theTestPassed = false; + testGeneratedInterrupt = true; + mainThread.interrupt(); + } +} + +/** + * This is part of the standard test machinery. It creates a dialog (with the + * instructions), and is the interface for sending text messages to the user. To + * print the instructions, send an array of strings to Sysout.createDialog + * WithInstructions method. Put one line of instructions per array entry. To + * display a message for the tester to see, simply call Sysout.println with the + * string to be displayed. This mimics System.out.println but works within the + * test harness as well as standalone. + */ +class Sysout { + + private static TestDialog dialog; + private static JFrame frame; + + public static void createDialogWithInstructions(String[] instructions) { + frame = new JFrame(); + dialog = new TestDialog(frame, "Instructions"); + dialog.printInstructions(instructions); + dialog.setVisible(true); + println("Any messages for the tester will display here."); + } + + public static void printInstructions(String[] instructions) { + dialog.printInstructions(instructions); + } + + public static void println(String messageIn) { + dialog.displayMessage(messageIn); + } + + public static void dispose() { + Sysout.println("Shutting down the Java process.."); + if(FileChooserTest.fileChooser != null) { + FileChooserTest.fileChooser.cancelSelection(); + } + frame.dispose(); + dialog.dispose(); + } +} + +/** + * This is part of the standard test machinery. It provides a place for the test + * instructions to be displayed, and a place for interactive messages to the + * user to be displayed. To have the test instructions displayed, see Sysout. To + * have a message to the user be displayed, see Sysout. Do not call anything in + * this dialog directly. + */ +class TestDialog extends JDialog { + + private TextArea instructionsText; + private TextArea messageText; + private int maxStringLength = 80; + private Panel buttonP = new Panel(); + private JButton run = new JButton("Run"); + private JButton passB = new JButton("Pass"); + private JButton failB = new JButton("Fail"); + + public TestDialog(JFrame frame, String name) { + super(frame, name); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + int scrollBoth = TextArea.SCROLLBARS_BOTH; + instructionsText = new TextArea("", 15, maxStringLength, scrollBoth); + add("North", instructionsText); + + messageText = new TextArea("", 5, maxStringLength, scrollBoth); + add("Center", messageText); + + buttonP.add("East", run); + buttonP.add("East", passB); + buttonP.add("West", failB); + passB.setEnabled(false); + failB.setEnabled(false); + add("South", buttonP); + + run.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + FileChooserTest.fileChooser = new JFileChooser(); + FileChooserTest.fileChooser.showOpenDialog(null); + passB.setEnabled(true); + failB.setEnabled(true); + } + }); + + passB.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + FileChooserTest.pass(); + } + }); + + failB.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + FileChooserTest.fail(); + } + }); + pack(); + + setVisible(true); + } + + public void printInstructions(String[] instructions) { + instructionsText.setText(""); + + String printStr, remainingStr; + for (String instruction : instructions) { + remainingStr = instruction; + while (remainingStr.length() > 0) { + if (remainingStr.length() >= maxStringLength) { + int posOfSpace = remainingStr. + lastIndexOf(' ', maxStringLength - 1); + + if (posOfSpace <= 0) { + posOfSpace = maxStringLength - 1; + } + + printStr = remainingStr.substring(0, posOfSpace + 1); + remainingStr = remainingStr.substring(posOfSpace + 1); + } else { + printStr = remainingStr; + remainingStr = ""; + } + instructionsText.append(printStr + "\n"); + } + } + + } + + public void displayMessage(String messageIn) { + messageText.append(messageIn + "\n"); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/swing/JMenuItem/8139169/ScreenMenuBarInputTwice.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/swing/JMenuItem/8139169/ScreenMenuBarInputTwice.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8139169 + * @summary verifies if TextArea gets input twice due to Apple's Screen Menubar + * @requires (os.family=="mac") + * @library ../../regtesthelpers + * @build Util + * @run main ScreenMenuBarInputTwice + */ +import java.awt.BorderLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import static java.awt.event.KeyEvent.VK_COMMA; +import static java.awt.event.KeyEvent.VK_META; +import static java.awt.event.KeyEvent.VK_SHIFT; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.text.BadLocationException; + +public class ScreenMenuBarInputTwice { + + public static final String TEST_STRING = "Check string"; + + private static Robot robot; + private static JFrame frame; + private static JPanel content; + private static JTextArea textArea; + private static JMenuBar menuBar; + private static JMenu menu; + private static JMenuItem menuItem; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + createUIWithSeperateMenuBar(); + robot.delay(2000); + shortcutTestCase(); + robot.delay(2000); + cleanUp(); + createUIWithIntegratedMenuBar(); + robot.delay(2000); + menuTestCase(); + robot.delay(2000); + cleanUp(); + } + + private static void createUIWithSeperateMenuBar() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + System.setProperty( + "com.apple.mrj.application.apple.menu.about.name", + "A test frame"); + System.setProperty("apple.laf.useScreenMenuBar", "true"); + frame = new JFrame("Text input twice check"); + content = new JPanel(new BorderLayout()); + textArea = new JTextArea(); + content.add(new JScrollPane(textArea, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), + BorderLayout.CENTER); + menuBar = new JMenuBar(); + frame.setJMenuBar(menuBar); + Action a = new AbstractAction("Insert some text") { + @Override + public void actionPerformed(ActionEvent arg0) { + try { + + textArea.getDocument() + .insertString(0, TEST_STRING, null); + } catch (BadLocationException e) { + frame.dispose(); + throw new RuntimeException("Bad location: ", e); + } + } + }; + KeyStroke keyStroke = KeyStroke.getKeyStroke( + "meta shift COMMA"); + a.putValue(Action.ACCELERATOR_KEY, keyStroke); + textArea.getInputMap().put(keyStroke, "myAction"); + textArea.getActionMap().put("myAction", a); + menu = new JMenu("The Menu"); + menuItem = new JMenuItem(a); + menuItem.setAccelerator((KeyStroke) a.getValue( + Action.ACCELERATOR_KEY)); + menu.add(menuItem); + menuBar.add(menu); + frame.getContentPane().add(content); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setLocationRelativeTo(null); + frame.setSize(500, 500); + frame.setVisible(true); + frame.toFront(); + } + }); + } + + private static void createUIWithIntegratedMenuBar() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + System.setProperty( + "com.apple.mrj.application.apple.menu.about.name", + "A test frame"); + System.setProperty("apple.laf.useScreenMenuBar", "false"); + frame = new JFrame("Text input twice check"); + content = new JPanel(new BorderLayout()); + textArea = new JTextArea(); + content.add(new JScrollPane(textArea, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), + BorderLayout.CENTER); + menuBar = new JMenuBar(); + frame.setJMenuBar(menuBar); + Action a = new AbstractAction("Insert some text") { + @Override + public void actionPerformed(ActionEvent arg0) { + try { + + textArea.getDocument() + .insertString(0, TEST_STRING, null); + } catch (BadLocationException e) { + frame.dispose(); + throw new RuntimeException("Bad location: ", e); + } + } + }; + KeyStroke keyStroke = KeyStroke.getKeyStroke( + "meta shift COMMA"); + a.putValue(Action.ACCELERATOR_KEY, keyStroke); + textArea.getInputMap().put(keyStroke, "myAction"); + textArea.getActionMap().put("myAction", a); + menu = new JMenu("The Menu"); + menuItem = new JMenuItem(a); + menuItem.setAccelerator((KeyStroke) a.getValue( + Action.ACCELERATOR_KEY)); + menu.add(menuItem); + menuBar.add(menu); + frame.getContentPane().add(content); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(500, 500); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + frame.toFront(); + } + }); + } + + private static void shortcutTestCase() throws Exception { + robot.keyPress(KeyEvent.VK_META); + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_COMMA); + robot.keyRelease(VK_COMMA); + robot.keyRelease(VK_SHIFT); + robot.keyRelease(VK_META); + robot.delay(2000); + checkText(textArea.getText()); + } + + private static void menuTestCase() throws Exception { + Point mousePoint; + mousePoint = Util.getCenterPoint(menu); + robot.mouseMove(mousePoint.x, mousePoint.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.delay(2000); + mousePoint = Util.getCenterPoint(menuItem); + robot.mouseMove(mousePoint.x, mousePoint.y); + robot.delay(2000); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.delay(2000); + checkText(textArea.getText()); + } + + private static void checkText(String text) throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if (TEST_STRING.equals(text)) { + textArea.setText(""); + } else { + frame.dispose(); + throw new RuntimeException("Failed. " + + " Menu item shortcut invoked twice"); + } + } + }); + } + + private static void cleanUp() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + frame.dispose(); + } + }); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/javax/swing/undo/UndoManager/AbstractDocumentUndoConcurrentTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/swing/undo/UndoManager/AbstractDocumentUndoConcurrentTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8030702 + @summary Deadlock between subclass of AbstractDocument and UndoManager + @author Semyon Sadetsky + */ + +import javax.swing.text.PlainDocument; +import javax.swing.text.StringContent; +import javax.swing.undo.UndoManager; +import java.text.DecimalFormat; +import java.text.Format; +import java.util.concurrent.CyclicBarrier; + +public class AbstractDocumentUndoConcurrentTest { + static CyclicBarrier barrier = new CyclicBarrier(3); + + private static PlainDocument doc1; + private static PlainDocument doc2; + private static Format format1 = new DecimalFormat(""); + private static Format format2 = new DecimalFormat(""); + + public static void main(String[] args) throws Exception { + test(); + System.out.println(doc1.getText(0, doc1.getLength())); + System.out.println(doc2.getText(0, doc2.getLength())); + System.out.println("ok"); + } + + private static void test() throws Exception { + doc1 = new PlainDocument(new StringContent()); + final UndoManager undoManager = new UndoManager(); + + doc1.addUndoableEditListener(undoManager); + doc1.insertString(0, "", null); + + doc2 = new PlainDocument(new StringContent()); + + doc2.addUndoableEditListener(undoManager); + doc2.insertString(0, "", null); + + Thread t1 = new Thread("Thread doc1") { + @Override + public void run() { + try { + barrier.await(); + for (int i = 0; i < 1000; i++) { + doc1.insertString(0, format1.format(i), null); + if(doc1.getLength() > 100) doc1.remove(0, 12); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + System.out.println("t1 done"); + } + }; + + Thread t2 = new Thread("Thread doc2") { + @Override + public void run() { + try { + barrier.await(); + for (int i = 0; i < 1000; i++) { + doc2.insertString(0, format2.format(i), null); + if(doc2.getLength() > 100) doc2.remove(0, 13); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + System.out.println("t2 done"); + } + }; + + Thread t3 = new Thread("Undo/Redo Thread") { + @Override + public void run() { + try { + barrier.await(); + } catch (Exception e) { + e.printStackTrace(); + } + for (int i = 0; i < 1000; i++) { + undoManager.undoOrRedo(); + undoManager.undo(); + } + System.out.println("t3 done"); + } + }; + + t1.start(); + t2.start(); + t3.start(); + + t1.join(); + t2.join(); + t3.join(); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/jdk/internal/math/FloatingDecimal/OldFDBigIntForTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/jdk/internal/math/FloatingDecimal/OldFDBigIntForTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,491 @@ +/* + * Copyright (c) 1996, 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 jdk.internal.math; + +/* + * A really, really simple bigint package + * tailored to the needs of floating base conversion. + */ +class OldFDBigIntForTest { + int nWords; // number of words used + int data[]; // value: data[0] is least significant + + + public OldFDBigIntForTest( int v ){ + nWords = 1; + data = new int[1]; + data[0] = v; + } + + public OldFDBigIntForTest( long v ){ + data = new int[2]; + data[0] = (int)v; + data[1] = (int)(v>>>32); + nWords = (data[1]==0) ? 1 : 2; + } + + public OldFDBigIntForTest( OldFDBigIntForTest other ){ + data = new int[nWords = other.nWords]; + System.arraycopy( other.data, 0, data, 0, nWords ); + } + + private OldFDBigIntForTest( int [] d, int n ){ + data = d; + nWords = n; + } + + public OldFDBigIntForTest( long seed, char digit[], int nd0, int nd ){ + int n= (nd+8)/9; // estimate size needed. + if ( n < 2 ) n = 2; + data = new int[n]; // allocate enough space + data[0] = (int)seed; // starting value + data[1] = (int)(seed>>>32); + nWords = (data[1]==0) ? 1 : 2; + int i = nd0; + int limit = nd-5; // slurp digits 5 at a time. + int v; + while ( i < limit ){ + int ilim = i+5; + v = (int)digit[i++]-(int)'0'; + while( i >5; + int bitcount = c & 0x1f; + int anticount = 32-bitcount; + int t[] = data; + int s[] = data; + if ( nWords+wordcount+1 > t.length ){ + // reallocate. + t = new int[ nWords+wordcount+1 ]; + } + int target = nWords+wordcount; + int src = nWords-1; + if ( bitcount == 0 ){ + // special hack, since an anticount of 32 won't go! + System.arraycopy( s, 0, t, wordcount, nWords ); + target = wordcount-1; + } else { + t[target--] = s[src]>>>anticount; + while ( src >= 1 ){ + t[target--] = (s[src]<>>anticount); + } + t[target--] = s[src]<= 0 ){ + t[target--] = 0; + } + data = t; + nWords += wordcount + 1; + // may have constructed high-order word of 0. + // if so, trim it + while ( nWords > 1 && data[nWords-1] == 0 ) + nWords--; + } + + /* + * normalize this number by shifting until + * the MSB of the number is at 0x08000000. + * This is in preparation for quoRemIteration, below. + * The idea is that, to make division easier, we want the + * divisor to be "normalized" -- usually this means shifting + * the MSB into the high words sign bit. But because we know that + * the quotient will be 0 < q < 10, we would like to arrange that + * the dividend not span up into another word of precision. + * (This needs to be explained more clearly!) + */ + public int + normalizeMe() throws IllegalArgumentException { + int src; + int wordcount = 0; + int bitcount = 0; + int v = 0; + for ( src= nWords-1 ; src >= 0 && (v=data[src]) == 0 ; src--){ + wordcount += 1; + } + if ( src < 0 ){ + // oops. Value is zero. Cannot normalize it! + throw new IllegalArgumentException("zero value"); + } + /* + * In most cases, we assume that wordcount is zero. This only + * makes sense, as we try not to maintain any high-order + * words full of zeros. In fact, if there are zeros, we will + * simply SHORTEN our number at this point. Watch closely... + */ + nWords -= wordcount; + /* + * Compute how far left we have to shift v s.t. its highest- + * order bit is in the right place. Then call lshiftMe to + * do the work. + */ + if ( (v & 0xf0000000) != 0 ){ + // will have to shift up into the next word. + // too bad. + for( bitcount = 32 ; (v & 0xf0000000) != 0 ; bitcount-- ) + v >>>= 1; + } else { + while ( v <= 0x000fffff ){ + // hack: byte-at-a-time shifting + v <<= 8; + bitcount += 8; + } + while ( v <= 0x07ffffff ){ + v <<= 1; + bitcount += 1; + } + } + if ( bitcount != 0 ) + lshiftMe( bitcount ); + return bitcount; + } + + /* + * Multiply a OldFDBigIntForTest by an int. + * Result is a new OldFDBigIntForTest. + */ + public OldFDBigIntForTest + mult( int iv ) { + long v = iv; + int r[]; + long p; + + // guess adequate size of r. + r = new int[ ( v * ((long)data[nWords-1]&0xffffffffL) > 0xfffffffL ) ? nWords+1 : nWords ]; + p = 0L; + for( int i=0; i < nWords; i++ ) { + p += v * ((long)data[i]&0xffffffffL); + r[i] = (int)p; + p >>>= 32; + } + if ( p == 0L){ + return new OldFDBigIntForTest( r, nWords ); + } else { + r[nWords] = (int)p; + return new OldFDBigIntForTest( r, nWords+1 ); + } + } + + /* + * Multiply a OldFDBigIntForTest by an int and add another int. + * Result is computed in place. + * Hope it fits! + */ + public void + multaddMe( int iv, int addend ) { + long v = iv; + long p; + + // unroll 0th iteration, doing addition. + p = v * ((long)data[0]&0xffffffffL) + ((long)addend&0xffffffffL); + data[0] = (int)p; + p >>>= 32; + for( int i=1; i < nWords; i++ ) { + p += v * ((long)data[i]&0xffffffffL); + data[i] = (int)p; + p >>>= 32; + } + if ( p != 0L){ + data[nWords] = (int)p; // will fail noisily if illegal! + nWords++; + } + } + + /* + * Multiply a OldFDBigIntForTest by another OldFDBigIntForTest. + * Result is a new OldFDBigIntForTest. + */ + public OldFDBigIntForTest + mult( OldFDBigIntForTest other ){ + // crudely guess adequate size for r + int r[] = new int[ nWords + other.nWords ]; + int i; + // I think I am promised zeros... + + for( i = 0; i < this.nWords; i++ ){ + long v = (long)this.data[i] & 0xffffffffL; // UNSIGNED CONVERSION + long p = 0L; + int j; + for( j = 0; j < other.nWords; j++ ){ + p += ((long)r[i+j]&0xffffffffL) + v*((long)other.data[j]&0xffffffffL); // UNSIGNED CONVERSIONS ALL 'ROUND. + r[i+j] = (int)p; + p >>>= 32; + } + r[i+j] = (int)p; + } + // compute how much of r we actually needed for all that. + for ( i = r.length-1; i> 0; i--) + if ( r[i] != 0 ) + break; + return new OldFDBigIntForTest( r, i+1 ); + } + + /* + * Add one OldFDBigIntForTest to another. Return a OldFDBigIntForTest + */ + public OldFDBigIntForTest + add( OldFDBigIntForTest other ){ + int i; + int a[], b[]; + int n, m; + long c = 0L; + // arrange such that a.nWords >= b.nWords; + // n = a.nWords, m = b.nWords + if ( this.nWords >= other.nWords ){ + a = this.data; + n = this.nWords; + b = other.data; + m = other.nWords; + } else { + a = other.data; + n = other.nWords; + b = this.data; + m = this.nWords; + } + int r[] = new int[ n ]; + for ( i = 0; i < n; i++ ){ + c += (long)a[i] & 0xffffffffL; + if ( i < m ){ + c += (long)b[i] & 0xffffffffL; + } + r[i] = (int) c; + c >>= 32; // signed shift. + } + if ( c != 0L ){ + // oops -- carry out -- need longer result. + int s[] = new int[ r.length+1 ]; + System.arraycopy( r, 0, s, 0, r.length ); + s[i++] = (int)c; + return new OldFDBigIntForTest( s, i ); + } + return new OldFDBigIntForTest( r, i ); + } + + /* + * Subtract one OldFDBigIntForTest from another. Return a OldFDBigIntForTest + * Assert that the result is positive. + */ + public OldFDBigIntForTest + sub( OldFDBigIntForTest other ){ + int r[] = new int[ this.nWords ]; + int i; + int n = this.nWords; + int m = other.nWords; + int nzeros = 0; + long c = 0L; + for ( i = 0; i < n; i++ ){ + c += (long)this.data[i] & 0xffffffffL; + if ( i < m ){ + c -= (long)other.data[i] & 0xffffffffL; + } + if ( ( r[i] = (int) c ) == 0 ) + nzeros++; + else + nzeros = 0; + c >>= 32; // signed shift + } + assert c == 0L : c; // borrow out of subtract + assert dataInRangeIsZero(i, m, other); // negative result of subtract + return new OldFDBigIntForTest( r, n-nzeros ); + } + + private static boolean dataInRangeIsZero(int i, int m, OldFDBigIntForTest other) { + while ( i < m ) + if (other.data[i++] != 0) + return false; + return true; + } + + /* + * Compare OldFDBigIntForTest with another OldFDBigIntForTest. Return an integer + * >0: this > other + * 0: this == other + * <0: this < other + */ + public int + cmp( OldFDBigIntForTest other ){ + int i; + if ( this.nWords > other.nWords ){ + // if any of my high-order words is non-zero, + // then the answer is evident + int j = other.nWords-1; + for ( i = this.nWords-1; i > j ; i-- ) + if ( this.data[i] != 0 ) return 1; + }else if ( this.nWords < other.nWords ){ + // if any of other's high-order words is non-zero, + // then the answer is evident + int j = this.nWords-1; + for ( i = other.nWords-1; i > j ; i-- ) + if ( other.data[i] != 0 ) return -1; + } else{ + i = this.nWords-1; + } + for ( ; i > 0 ; i-- ) + if ( this.data[i] != other.data[i] ) + break; + // careful! want unsigned compare! + // use brute force here. + int a = this.data[i]; + int b = other.data[i]; + if ( a < 0 ){ + // a is really big, unsigned + if ( b < 0 ){ + return a-b; // both big, negative + } else { + return 1; // b not big, answer is obvious; + } + } else { + // a is not really big + if ( b < 0 ) { + // but b is really big + return -1; + } else { + return a - b; + } + } + } + + /* + * Compute + * q = (int)( this / S ) + * this = 10 * ( this mod S ) + * Return q. + * This is the iteration step of digit development for output. + * We assume that S has been normalized, as above, and that + * "this" has been lshift'ed accordingly. + * Also assume, of course, that the result, q, can be expressed + * as an integer, 0 <= q < 10. + */ + public int + quoRemIteration( OldFDBigIntForTest S )throws IllegalArgumentException { + // ensure that this and S have the same number of + // digits. If S is properly normalized and q < 10 then + // this must be so. + if ( nWords != S.nWords ){ + throw new IllegalArgumentException("disparate values"); + } + // estimate q the obvious way. We will usually be + // right. If not, then we're only off by a little and + // will re-add. + int n = nWords-1; + long q = ((long)data[n]&0xffffffffL) / (long)S.data[n]; + long diff = 0L; + for ( int i = 0; i <= n ; i++ ){ + diff += ((long)data[i]&0xffffffffL) - q*((long)S.data[i]&0xffffffffL); + data[i] = (int)diff; + diff >>= 32; // N.B. SIGNED shift. + } + if ( diff != 0L ) { + // damn, damn, damn. q is too big. + // add S back in until this turns +. This should + // not be very many times! + long sum = 0L; + while ( sum == 0L ){ + sum = 0L; + for ( int i = 0; i <= n; i++ ){ + sum += ((long)data[i]&0xffffffffL) + ((long)S.data[i]&0xffffffffL); + data[i] = (int) sum; + sum >>= 32; // Signed or unsigned, answer is 0 or 1 + } + /* + * Originally the following line read + * "if ( sum !=0 && sum != -1 )" + * but that would be wrong, because of the + * treatment of the two values as entirely unsigned, + * it would be impossible for a carry-out to be interpreted + * as -1 -- it would have to be a single-bit carry-out, or + * +1. + */ + assert sum == 0 || sum == 1 : sum; // carry out of division correction + q -= 1; + } + } + // finally, we can multiply this by 10. + // it cannot overflow, right, as the high-order word has + // at least 4 high-order zeros! + long p = 0L; + for ( int i = 0; i <= n; i++ ){ + p += 10*((long)data[i]&0xffffffffL); + data[i] = (int)p; + p >>= 32; // SIGNED shift. + } + assert p == 0L : p; // Carry out of *10 + return (int)q; + } + + public long + longValue(){ + // if this can be represented as a long, return the value + assert this.nWords > 0 : this.nWords; // longValue confused + + if (this.nWords == 1) + return ((long)data[0]&0xffffffffL); + + assert dataInRangeIsZero(2, this.nWords, this); // value too big + assert data[1] >= 0; // value too big + return ((long)(data[1]) << 32) | ((long)data[0]&0xffffffffL); + } + + public String + toString() { + StringBuffer r = new StringBuffer(30); + r.append('['); + int i = Math.min( nWords-1, data.length-1) ; + if ( nWords > data.length ){ + r.append( "("+data.length+"<"+nWords+"!)" ); + } + for( ; i> 0 ; i-- ){ + r.append( Integer.toHexString( data[i] ) ); + r.append(' '); + } + r.append( Integer.toHexString( data[0] ) ); + r.append(']'); + return new String( r ); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/jdk/internal/math/FloatingDecimal/OldFloatingDecimalForTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/jdk/internal/math/FloatingDecimal/OldFloatingDecimalForTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,2434 @@ +/* + * Copyright (c) 1996, 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 jdk.internal.math; + +import java.util.regex.*; + +public class OldFloatingDecimalForTest{ + boolean isExceptional; + boolean isNegative; + int decExponent; + char digits[]; + int nDigits; + int bigIntExp; + int bigIntNBits; + boolean mustSetRoundDir = false; + boolean fromHex = false; + int roundDir = 0; // set by doubleValue + + /* + * The fields below provides additional information about the result of + * the binary to decimal digits conversion done in dtoa() and roundup() + * methods. They are changed if needed by those two methods. + */ + + // True if the dtoa() binary to decimal conversion was exact. + boolean exactDecimalConversion = false; + + // True if the result of the binary to decimal conversion was rounded-up + // at the end of the conversion process, i.e. roundUp() method was called. + boolean decimalDigitsRoundedUp = false; + + private OldFloatingDecimalForTest( boolean negSign, int decExponent, char []digits, int n, boolean e ) + { + isNegative = negSign; + isExceptional = e; + this.decExponent = decExponent; + this.digits = digits; + this.nDigits = n; + } + + /* + * Constants of the implementation + * Most are IEEE-754 related. + * (There are more really boring constants at the end.) + */ + static final long signMask = 0x8000000000000000L; + static final long expMask = 0x7ff0000000000000L; + static final long fractMask= ~(signMask|expMask); + static final int expShift = 52; + static final int expBias = 1023; + static final long fractHOB = ( 1L< 0L ) { // i.e. while ((v&highbit) == 0L ) + v <<= 1; + } + + int n = 0; + while (( v & lowbytes ) != 0L ){ + v <<= 8; + n += 8; + } + while ( v != 0L ){ + v <<= 1; + n += 1; + } + return n; + } + + /* + * Keep big powers of 5 handy for future reference. + */ + private static OldFDBigIntForTest b5p[]; + + private static synchronized OldFDBigIntForTest + big5pow( int p ){ + assert p >= 0 : p; // negative power of 5 + if ( b5p == null ){ + b5p = new OldFDBigIntForTest[ p+1 ]; + }else if (b5p.length <= p ){ + OldFDBigIntForTest t[] = new OldFDBigIntForTest[ p+1 ]; + System.arraycopy( b5p, 0, t, 0, b5p.length ); + b5p = t; + } + if ( b5p[p] != null ) + return b5p[p]; + else if ( p < small5pow.length ) + return b5p[p] = new OldFDBigIntForTest( small5pow[p] ); + else if ( p < long5pow.length ) + return b5p[p] = new OldFDBigIntForTest( long5pow[p] ); + else { + // construct the value. + // recursively. + int q, r; + // in order to compute 5^p, + // compute its square root, 5^(p/2) and square. + // or, let q = p / 2, r = p -q, then + // 5^p = 5^(q+r) = 5^q * 5^r + q = p >> 1; + r = p - q; + OldFDBigIntForTest bigq = b5p[q]; + if ( bigq == null ) + bigq = big5pow ( q ); + if ( r < small5pow.length ){ + return (b5p[p] = bigq.mult( small5pow[r] ) ); + }else{ + OldFDBigIntForTest bigr = b5p[ r ]; + if ( bigr == null ) + bigr = big5pow( r ); + return (b5p[p] = bigq.mult( bigr ) ); + } + } + } + + // + // a common operation + // + private static OldFDBigIntForTest + multPow52( OldFDBigIntForTest v, int p5, int p2 ){ + if ( p5 != 0 ){ + if ( p5 < small5pow.length ){ + v = v.mult( small5pow[p5] ); + } else { + v = v.mult( big5pow( p5 ) ); + } + } + if ( p2 != 0 ){ + v.lshiftMe( p2 ); + } + return v; + } + + // + // another common operation + // + private static OldFDBigIntForTest + constructPow52( int p5, int p2 ){ + OldFDBigIntForTest v = new OldFDBigIntForTest( big5pow( p5 ) ); + if ( p2 != 0 ){ + v.lshiftMe( p2 ); + } + return v; + } + + /* + * Make a floating double into a OldFDBigIntForTest. + * This could also be structured as a OldFDBigIntForTest + * constructor, but we'd have to build a lot of knowledge + * about floating-point representation into it, and we don't want to. + * + * AS A SIDE EFFECT, THIS METHOD WILL SET THE INSTANCE VARIABLES + * bigIntExp and bigIntNBits + * + */ + private OldFDBigIntForTest + doubleToBigInt( double dval ){ + long lbits = Double.doubleToLongBits( dval ) & ~signMask; + int binexp = (int)(lbits >>> expShift); + lbits &= fractMask; + if ( binexp > 0 ){ + lbits |= fractHOB; + } else { + assert lbits != 0L : lbits; // doubleToBigInt(0.0) + binexp +=1; + while ( (lbits & fractHOB ) == 0L){ + lbits <<= 1; + binexp -= 1; + } + } + binexp -= expBias; + int nbits = countBits( lbits ); + /* + * We now know where the high-order 1 bit is, + * and we know how many there are. + */ + int lowOrderZeros = expShift+1-nbits; + lbits >>>= lowOrderZeros; + + bigIntExp = binexp+1-nbits; + bigIntNBits = nbits; + return new OldFDBigIntForTest( lbits ); + } + + /* + * Compute a number that is the ULP of the given value, + * for purposes of addition/subtraction. Generally easy. + * More difficult if subtracting and the argument + * is a normalized a power of 2, as the ULP changes at these points. + */ + private static double ulp( double dval, boolean subtracting ){ + long lbits = Double.doubleToLongBits( dval ) & ~signMask; + int binexp = (int)(lbits >>> expShift); + double ulpval; + if ( subtracting && ( binexp >= expShift ) && ((lbits&fractMask) == 0L) ){ + // for subtraction from normalized, powers of 2, + // use next-smaller exponent + binexp -= 1; + } + if ( binexp > expShift ){ + ulpval = Double.longBitsToDouble( ((long)(binexp-expShift))< 0L (not zero, nor negative). + * + * The only reason that we develop the digits here, rather than + * calling on Long.toString() is that we can do it a little faster, + * and besides want to treat trailing 0s specially. If Long.toString + * changes, we should re-evaluate this strategy! + */ + private void + developLongDigits( int decExponent, long lvalue, long insignificant ){ + char digits[]; + int ndigits; + int digitno; + int c; + // + // Discard non-significant low-order bits, while rounding, + // up to insignificant value. + int i; + for ( i = 0; insignificant >= 10L; i++ ) + insignificant /= 10L; + if ( i != 0 ){ + long pow10 = long5pow[i] << i; // 10^i == 5^i * 2^i; + long residue = lvalue % pow10; + lvalue /= pow10; + decExponent += i; + if ( residue >= (pow10>>1) ){ + // round up based on the low-order bits we're discarding + lvalue++; + } + } + if ( lvalue <= Integer.MAX_VALUE ){ + assert lvalue > 0L : lvalue; // lvalue <= 0 + // even easier subcase! + // can do int arithmetic rather than long! + int ivalue = (int)lvalue; + ndigits = 10; + digits = perThreadBuffer.get(); + digitno = ndigits-1; + c = ivalue%10; + ivalue /= 10; + while ( c == 0 ){ + decExponent++; + c = ivalue%10; + ivalue /= 10; + } + while ( ivalue != 0){ + digits[digitno--] = (char)(c+'0'); + decExponent++; + c = ivalue%10; + ivalue /= 10; + } + digits[digitno] = (char)(c+'0'); + } else { + // same algorithm as above (same bugs, too ) + // but using long arithmetic. + ndigits = 20; + digits = perThreadBuffer.get(); + digitno = ndigits-1; + c = (int)(lvalue%10L); + lvalue /= 10L; + while ( c == 0 ){ + decExponent++; + c = (int)(lvalue%10L); + lvalue /= 10L; + } + while ( lvalue != 0L ){ + digits[digitno--] = (char)(c+'0'); + decExponent++; + c = (int)(lvalue%10L); + lvalue /= 10; + } + digits[digitno] = (char)(c+'0'); + } + char result []; + ndigits -= digitno; + result = new char[ ndigits ]; + System.arraycopy( digits, digitno, result, 0, ndigits ); + this.digits = result; + this.decExponent = decExponent+1; + this.nDigits = ndigits; + } + + // + // add one to the least significant digit. + // in the unlikely event there is a carry out, + // deal with it. + // assert that this will only happen where there + // is only one digit, e.g. (float)1e-44 seems to do it. + // + private void + roundup(){ + int i; + int q = digits[ i = (nDigits-1)]; + if ( q == '9' ){ + while ( q == '9' && i > 0 ){ + digits[i] = '0'; + q = digits[--i]; + } + if ( q == '9' ){ + // carryout! High-order 1, rest 0s, larger exp. + decExponent += 1; + digits[0] = '1'; + return; + } + // else fall through. + } + digits[i] = (char)(q+1); + decimalDigitsRoundedUp = true; + } + + public boolean digitsRoundedUp() { + return decimalDigitsRoundedUp; + } + + /* + * FIRST IMPORTANT CONSTRUCTOR: DOUBLE + */ + public OldFloatingDecimalForTest( double d ) + { + long dBits = Double.doubleToLongBits( d ); + long fractBits; + int binExp; + int nSignificantBits; + + // discover and delete sign + if ( (dBits&signMask) != 0 ){ + isNegative = true; + dBits ^= signMask; + } else { + isNegative = false; + } + // Begin to unpack + // Discover obvious special cases of NaN and Infinity. + binExp = (int)( (dBits&expMask) >> expShift ); + fractBits = dBits&fractMask; + if ( binExp == (int)(expMask>>expShift) ) { + isExceptional = true; + if ( fractBits == 0L ){ + digits = infinity; + } else { + digits = notANumber; + isNegative = false; // NaN has no sign! + } + nDigits = digits.length; + return; + } + isExceptional = false; + // Finish unpacking + // Normalize denormalized numbers. + // Insert assumed high-order bit for normalized numbers. + // Subtract exponent bias. + if ( binExp == 0 ){ + if ( fractBits == 0L ){ + // not a denorm, just a 0! + decExponent = 0; + digits = zero; + nDigits = 1; + return; + } + while ( (fractBits&fractHOB) == 0L ){ + fractBits <<= 1; + binExp -= 1; + } + nSignificantBits = expShift + binExp +1; // recall binExp is - shift count. + binExp += 1; + } else { + fractBits |= fractHOB; + nSignificantBits = expShift+1; + } + binExp -= expBias; + // call the routine that actually does all the hard work. + dtoa( binExp, fractBits, nSignificantBits ); + } + + /* + * SECOND IMPORTANT CONSTRUCTOR: SINGLE + */ + public OldFloatingDecimalForTest( float f ) + { + int fBits = Float.floatToIntBits( f ); + int fractBits; + int binExp; + int nSignificantBits; + + // discover and delete sign + if ( (fBits&singleSignMask) != 0 ){ + isNegative = true; + fBits ^= singleSignMask; + } else { + isNegative = false; + } + // Begin to unpack + // Discover obvious special cases of NaN and Infinity. + binExp = (fBits&singleExpMask) >> singleExpShift; + fractBits = fBits&singleFractMask; + if ( binExp == (singleExpMask>>singleExpShift) ) { + isExceptional = true; + if ( fractBits == 0L ){ + digits = infinity; + } else { + digits = notANumber; + isNegative = false; // NaN has no sign! + } + nDigits = digits.length; + return; + } + isExceptional = false; + // Finish unpacking + // Normalize denormalized numbers. + // Insert assumed high-order bit for normalized numbers. + // Subtract exponent bias. + if ( binExp == 0 ){ + if ( fractBits == 0 ){ + // not a denorm, just a 0! + decExponent = 0; + digits = zero; + nDigits = 1; + return; + } + while ( (fractBits&singleFractHOB) == 0 ){ + fractBits <<= 1; + binExp -= 1; + } + nSignificantBits = singleExpShift + binExp +1; // recall binExp is - shift count. + binExp += 1; + } else { + fractBits |= singleFractHOB; + nSignificantBits = singleExpShift+1; + } + binExp -= singleExpBias; + // call the routine that actually does all the hard work. + dtoa( binExp, ((long)fractBits)<<(expShift-singleExpShift), nSignificantBits ); + } + + private void + dtoa( int binExp, long fractBits, int nSignificantBits ) + { + int nFractBits; // number of significant bits of fractBits; + int nTinyBits; // number of these to the right of the point. + int decExp; + + // Examine number. Determine if it is an easy case, + // which we can do pretty trivially using float/long conversion, + // or whether we must do real work. + nFractBits = countBits( fractBits ); + nTinyBits = Math.max( 0, nFractBits - binExp - 1 ); + if ( binExp <= maxSmallBinExp && binExp >= minSmallBinExp ){ + // Look more closely at the number to decide if, + // with scaling by 10^nTinyBits, the result will fit in + // a long. + if ( (nTinyBits < long5pow.length) && ((nFractBits + n5bits[nTinyBits]) < 64 ) ){ + /* + * We can do this: + * take the fraction bits, which are normalized. + * (a) nTinyBits == 0: Shift left or right appropriately + * to align the binary point at the extreme right, i.e. + * where a long int point is expected to be. The integer + * result is easily converted to a string. + * (b) nTinyBits > 0: Shift right by expShift-nFractBits, + * which effectively converts to long and scales by + * 2^nTinyBits. Then multiply by 5^nTinyBits to + * complete the scaling. We know this won't overflow + * because we just counted the number of bits necessary + * in the result. The integer you get from this can + * then be converted to a string pretty easily. + */ + long halfULP; + if ( nTinyBits == 0 ) { + if ( binExp > nSignificantBits ){ + halfULP = 1L << ( binExp-nSignificantBits-1); + } else { + halfULP = 0L; + } + if ( binExp >= expShift ){ + fractBits <<= (binExp-expShift); + } else { + fractBits >>>= (expShift-binExp) ; + } + developLongDigits( 0, fractBits, halfULP ); + return; + } + /* + * The following causes excess digits to be printed + * out in the single-float case. Our manipulation of + * halfULP here is apparently not correct. If we + * better understand how this works, perhaps we can + * use this special case again. But for the time being, + * we do not. + * else { + * fractBits >>>= expShift+1-nFractBits; + * fractBits *= long5pow[ nTinyBits ]; + * halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits); + * developLongDigits( -nTinyBits, fractBits, halfULP ); + * return; + * } + */ + } + } + /* + * This is the hard case. We are going to compute large positive + * integers B and S and integer decExp, s.t. + * d = ( B / S ) * 10^decExp + * 1 <= B / S < 10 + * Obvious choices are: + * decExp = floor( log10(d) ) + * B = d * 2^nTinyBits * 10^max( 0, -decExp ) + * S = 10^max( 0, decExp) * 2^nTinyBits + * (noting that nTinyBits has already been forced to non-negative) + * I am also going to compute a large positive integer + * M = (1/2^nSignificantBits) * 2^nTinyBits * 10^max( 0, -decExp ) + * i.e. M is (1/2) of the ULP of d, scaled like B. + * When we iterate through dividing B/S and picking off the + * quotient bits, we will know when to stop when the remainder + * is <= M. + * + * We keep track of powers of 2 and powers of 5. + */ + + /* + * Estimate decimal exponent. (If it is small-ish, + * we could double-check.) + * + * First, scale the mantissa bits such that 1 <= d2 < 2. + * We are then going to estimate + * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5) + * and so we can estimate + * log10(d) ~=~ log10(d2) + binExp * log10(2) + * take the floor and call it decExp. + * FIXME -- use more precise constants here. It costs no more. + */ + double d2 = Double.longBitsToDouble( + expOne | ( fractBits &~ fractHOB ) ); + decExp = (int)Math.floor( + (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981 ); + int B2, B5; // powers of 2 and powers of 5, respectively, in B + int S2, S5; // powers of 2 and powers of 5, respectively, in S + int M2, M5; // powers of 2 and powers of 5, respectively, in M + int Bbits; // binary digits needed to represent B, approx. + int tenSbits; // binary digits needed to represent 10*S, approx. + OldFDBigIntForTest Sval, Bval, Mval; + + B5 = Math.max( 0, -decExp ); + B2 = B5 + nTinyBits + binExp; + + S5 = Math.max( 0, decExp ); + S2 = S5 + nTinyBits; + + M5 = B5; + M2 = B2 - nSignificantBits; + + /* + * the long integer fractBits contains the (nFractBits) interesting + * bits from the mantissa of d ( hidden 1 added if necessary) followed + * by (expShift+1-nFractBits) zeros. In the interest of compactness, + * I will shift out those zeros before turning fractBits into a + * OldFDBigIntForTest. The resulting whole number will be + * d * 2^(nFractBits-1-binExp). + */ + fractBits >>>= (expShift+1-nFractBits); + B2 -= nFractBits-1; + int common2factor = Math.min( B2, S2 ); + B2 -= common2factor; + S2 -= common2factor; + M2 -= common2factor; + + /* + * HACK!! For exact powers of two, the next smallest number + * is only half as far away as we think (because the meaning of + * ULP changes at power-of-two bounds) for this reason, we + * hack M2. Hope this works. + */ + if ( nFractBits == 1 ) + M2 -= 1; + + if ( M2 < 0 ){ + // oops. + // since we cannot scale M down far enough, + // we must scale the other values up. + B2 -= M2; + S2 -= M2; + M2 = 0; + } + /* + * Construct, Scale, iterate. + * Some day, we'll write a stopping test that takes + * account of the asymmetry of the spacing of floating-point + * numbers below perfect powers of 2 + * 26 Sept 96 is not that day. + * So we use a symmetric test. + */ + char digits[] = this.digits = new char[18]; + int ndigit = 0; + boolean low, high; + long lowDigitDifference; + int q; + + /* + * Detect the special cases where all the numbers we are about + * to compute will fit in int or long integers. + * In these cases, we will avoid doing OldFDBigIntForTest arithmetic. + * We use the same algorithms, except that we "normalize" + * our OldFDBigIntForTests before iterating. This is to make division easier, + * as it makes our fist guess (quotient of high-order words) + * more accurate! + * + * Some day, we'll write a stopping test that takes + * account of the asymmetry of the spacing of floating-point + * numbers below perfect powers of 2 + * 26 Sept 96 is not that day. + * So we use a symmetric test. + */ + Bbits = nFractBits + B2 + (( B5 < n5bits.length )? n5bits[B5] : ( B5*3 )); + tenSbits = S2+1 + (( (S5+1) < n5bits.length )? n5bits[(S5+1)] : ( (S5+1)*3 )); + if ( Bbits < 64 && tenSbits < 64){ + if ( Bbits < 32 && tenSbits < 32){ + // wa-hoo! They're all ints! + int b = ((int)fractBits * small5pow[B5] ) << B2; + int s = small5pow[S5] << S2; + int m = small5pow[M5] << M2; + int tens = s * 10; + /* + * Unroll the first iteration. If our decExp estimate + * was too high, our first quotient will be zero. In this + * case, we discard it and decrement decExp. + */ + ndigit = 0; + q = b / s; + b = 10 * ( b % s ); + m *= 10; + low = (b < m ); + high = (b+m > tens ); + assert q < 10 : q; // excessively large digit + if ( (q == 0) && ! high ){ + // oops. Usually ignore leading zero. + decExp--; + } else { + digits[ndigit++] = (char)('0' + q); + } + /* + * HACK! Java spec sez that we always have at least + * one digit after the . in either F- or E-form output. + * Thus we will need more than one digit if we're using + * E-form + */ + if ( decExp < -3 || decExp >= 8 ){ + high = low = false; + } + while( ! low && ! high ){ + q = b / s; + b = 10 * ( b % s ); + m *= 10; + assert q < 10 : q; // excessively large digit + if ( m > 0L ){ + low = (b < m ); + high = (b+m > tens ); + } else { + // hack -- m might overflow! + // in this case, it is certainly > b, + // which won't + // and b+m > tens, too, since that has overflowed + // either! + low = true; + high = true; + } + digits[ndigit++] = (char)('0' + q); + } + lowDigitDifference = (b<<1) - tens; + exactDecimalConversion = (b == 0); + } else { + // still good! they're all longs! + long b = (fractBits * long5pow[B5] ) << B2; + long s = long5pow[S5] << S2; + long m = long5pow[M5] << M2; + long tens = s * 10L; + /* + * Unroll the first iteration. If our decExp estimate + * was too high, our first quotient will be zero. In this + * case, we discard it and decrement decExp. + */ + ndigit = 0; + q = (int) ( b / s ); + b = 10L * ( b % s ); + m *= 10L; + low = (b < m ); + high = (b+m > tens ); + assert q < 10 : q; // excessively large digit + if ( (q == 0) && ! high ){ + // oops. Usually ignore leading zero. + decExp--; + } else { + digits[ndigit++] = (char)('0' + q); + } + /* + * HACK! Java spec sez that we always have at least + * one digit after the . in either F- or E-form output. + * Thus we will need more than one digit if we're using + * E-form + */ + if ( decExp < -3 || decExp >= 8 ){ + high = low = false; + } + while( ! low && ! high ){ + q = (int) ( b / s ); + b = 10 * ( b % s ); + m *= 10; + assert q < 10 : q; // excessively large digit + if ( m > 0L ){ + low = (b < m ); + high = (b+m > tens ); + } else { + // hack -- m might overflow! + // in this case, it is certainly > b, + // which won't + // and b+m > tens, too, since that has overflowed + // either! + low = true; + high = true; + } + digits[ndigit++] = (char)('0' + q); + } + lowDigitDifference = (b<<1) - tens; + exactDecimalConversion = (b == 0); + } + } else { + OldFDBigIntForTest ZeroVal = new OldFDBigIntForTest(0); + OldFDBigIntForTest tenSval; + int shiftBias; + + /* + * We really must do OldFDBigIntForTest arithmetic. + * Fist, construct our OldFDBigIntForTest initial values. + */ + Bval = multPow52( new OldFDBigIntForTest( fractBits ), B5, B2 ); + Sval = constructPow52( S5, S2 ); + Mval = constructPow52( M5, M2 ); + + + // normalize so that division works better + Bval.lshiftMe( shiftBias = Sval.normalizeMe() ); + Mval.lshiftMe( shiftBias ); + tenSval = Sval.mult( 10 ); + /* + * Unroll the first iteration. If our decExp estimate + * was too high, our first quotient will be zero. In this + * case, we discard it and decrement decExp. + */ + ndigit = 0; + q = Bval.quoRemIteration( Sval ); + Mval = Mval.mult( 10 ); + low = (Bval.cmp( Mval ) < 0); + high = (Bval.add( Mval ).cmp( tenSval ) > 0 ); + assert q < 10 : q; // excessively large digit + if ( (q == 0) && ! high ){ + // oops. Usually ignore leading zero. + decExp--; + } else { + digits[ndigit++] = (char)('0' + q); + } + /* + * HACK! Java spec sez that we always have at least + * one digit after the . in either F- or E-form output. + * Thus we will need more than one digit if we're using + * E-form + */ + if ( decExp < -3 || decExp >= 8 ){ + high = low = false; + } + while( ! low && ! high ){ + q = Bval.quoRemIteration( Sval ); + Mval = Mval.mult( 10 ); + assert q < 10 : q; // excessively large digit + low = (Bval.cmp( Mval ) < 0); + high = (Bval.add( Mval ).cmp( tenSval ) > 0 ); + digits[ndigit++] = (char)('0' + q); + } + if ( high && low ){ + Bval.lshiftMe(1); + lowDigitDifference = Bval.cmp(tenSval); + } else { + lowDigitDifference = 0L; // this here only for flow analysis! + } + exactDecimalConversion = (Bval.cmp( ZeroVal ) == 0); + } + this.decExponent = decExp+1; + this.digits = digits; + this.nDigits = ndigit; + /* + * Last digit gets rounded based on stopping condition. + */ + if ( high ){ + if ( low ){ + if ( lowDigitDifference == 0L ){ + // it's a tie! + // choose based on which digits we like. + if ( (digits[nDigits-1]&1) != 0 ) roundup(); + } else if ( lowDigitDifference > 0 ){ + roundup(); + } + } else { + roundup(); + } + } + } + + public boolean decimalDigitsExact() { + return exactDecimalConversion; + } + + public String + toString(){ + // most brain-dead version + StringBuffer result = new StringBuffer( nDigits+8 ); + if ( isNegative ){ result.append( '-' ); } + if ( isExceptional ){ + result.append( digits, 0, nDigits ); + } else { + result.append( "0."); + result.append( digits, 0, nDigits ); + result.append('e'); + result.append( decExponent ); + } + return new String(result); + } + + public String toJavaFormatString() { + char result[] = perThreadBuffer.get(); + int i = getChars(result); + return new String(result, 0, i); + } + + private int getChars(char[] result) { + assert nDigits <= 19 : nDigits; // generous bound on size of nDigits + int i = 0; + if (isNegative) { result[0] = '-'; i = 1; } + if (isExceptional) { + System.arraycopy(digits, 0, result, i, nDigits); + i += nDigits; + } else { + if (decExponent > 0 && decExponent < 8) { + // print digits.digits. + int charLength = Math.min(nDigits, decExponent); + System.arraycopy(digits, 0, result, i, charLength); + i += charLength; + if (charLength < decExponent) { + charLength = decExponent-charLength; + System.arraycopy(zero, 0, result, i, charLength); + i += charLength; + result[i++] = '.'; + result[i++] = '0'; + } else { + result[i++] = '.'; + if (charLength < nDigits) { + int t = nDigits - charLength; + System.arraycopy(digits, charLength, result, i, t); + i += t; + } else { + result[i++] = '0'; + } + } + } else if (decExponent <=0 && decExponent > -3) { + result[i++] = '0'; + result[i++] = '.'; + if (decExponent != 0) { + System.arraycopy(zero, 0, result, i, -decExponent); + i -= decExponent; + } + System.arraycopy(digits, 0, result, i, nDigits); + i += nDigits; + } else { + result[i++] = digits[0]; + result[i++] = '.'; + if (nDigits > 1) { + System.arraycopy(digits, 1, result, i, nDigits-1); + i += nDigits-1; + } else { + result[i++] = '0'; + } + result[i++] = 'E'; + int e; + if (decExponent <= 0) { + result[i++] = '-'; + e = -decExponent+1; + } else { + e = decExponent-1; + } + // decExponent has 1, 2, or 3, digits + if (e <= 9) { + result[i++] = (char)(e+'0'); + } else if (e <= 99) { + result[i++] = (char)(e/10 +'0'); + result[i++] = (char)(e%10 + '0'); + } else { + result[i++] = (char)(e/100+'0'); + e %= 100; + result[i++] = (char)(e/10+'0'); + result[i++] = (char)(e%10 + '0'); + } + } + } + return i; + } + + // Per-thread buffer for string/stringbuffer conversion + private static ThreadLocal perThreadBuffer = new ThreadLocal() { + protected synchronized char[] initialValue() { + return new char[26]; + } + }; + + public void appendTo(Appendable buf) { + char result[] = perThreadBuffer.get(); + int i = getChars(result); + if (buf instanceof StringBuilder) + ((StringBuilder) buf).append(result, 0, i); + else if (buf instanceof StringBuffer) + ((StringBuffer) buf).append(result, 0, i); + else + assert false; + } + + @SuppressWarnings("fallthrough") + public static OldFloatingDecimalForTest + readJavaFormatString( String in ) throws NumberFormatException { + boolean isNegative = false; + boolean signSeen = false; + int decExp; + char c; + + parseNumber: + try{ + in = in.trim(); // don't fool around with white space. + // throws NullPointerException if null + int l = in.length(); + if ( l == 0 ) throw new NumberFormatException("empty String"); + int i = 0; + switch ( c = in.charAt( i ) ){ + case '-': + isNegative = true; + //FALLTHROUGH + case '+': + i++; + signSeen = true; + } + + // Check for NaN and Infinity strings + c = in.charAt(i); + if(c == 'N' || c == 'I') { // possible NaN or infinity + boolean potentialNaN = false; + char targetChars[] = null; // char array of "NaN" or "Infinity" + + if(c == 'N') { + targetChars = notANumber; + potentialNaN = true; + } else { + targetChars = infinity; + } + + // compare Input string to "NaN" or "Infinity" + int j = 0; + while(i < l && j < targetChars.length) { + if(in.charAt(i) == targetChars[j]) { + i++; j++; + } + else // something is amiss, throw exception + break parseNumber; + } + + // For the candidate string to be a NaN or infinity, + // all characters in input string and target char[] + // must be matched ==> j must equal targetChars.length + // and i must equal l + if( (j == targetChars.length) && (i == l) ) { // return NaN or infinity + return (potentialNaN ? new OldFloatingDecimalForTest(Double.NaN) // NaN has no sign + : new OldFloatingDecimalForTest(isNegative? + Double.NEGATIVE_INFINITY: + Double.POSITIVE_INFINITY)) ; + } + else { // something went wrong, throw exception + break parseNumber; + } + + } else if (c == '0') { // check for hexadecimal floating-point number + if (l > i+1 ) { + char ch = in.charAt(i+1); + if (ch == 'x' || ch == 'X' ) // possible hex string + return parseHexString(in); + } + } // look for and process decimal floating-point string + + char[] digits = new char[ l ]; + int nDigits= 0; + boolean decSeen = false; + int decPt = 0; + int nLeadZero = 0; + int nTrailZero= 0; + digitLoop: + while ( i < l ){ + switch ( c = in.charAt( i ) ){ + case '0': + if ( nDigits > 0 ){ + nTrailZero += 1; + } else { + nLeadZero += 1; + } + break; // out of switch. + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + while ( nTrailZero > 0 ){ + digits[nDigits++] = '0'; + nTrailZero -= 1; + } + digits[nDigits++] = c; + break; // out of switch. + case '.': + if ( decSeen ){ + // already saw one ., this is the 2nd. + throw new NumberFormatException("multiple points"); + } + decPt = i; + if ( signSeen ){ + decPt -= 1; + } + decSeen = true; + break; // out of switch. + default: + break digitLoop; + } + i++; + } + /* + * At this point, we've scanned all the digits and decimal + * point we're going to see. Trim off leading and trailing + * zeros, which will just confuse us later, and adjust + * our initial decimal exponent accordingly. + * To review: + * we have seen i total characters. + * nLeadZero of them were zeros before any other digits. + * nTrailZero of them were zeros after any other digits. + * if ( decSeen ), then a . was seen after decPt characters + * ( including leading zeros which have been discarded ) + * nDigits characters were neither lead nor trailing + * zeros, nor point + */ + /* + * special hack: if we saw no non-zero digits, then the + * answer is zero! + * Unfortunately, we feel honor-bound to keep parsing! + */ + if ( nDigits == 0 ){ + digits = zero; + nDigits = 1; + if ( nLeadZero == 0 ){ + // we saw NO DIGITS AT ALL, + // not even a crummy 0! + // this is not allowed. + break parseNumber; // go throw exception + } + + } + + /* Our initial exponent is decPt, adjusted by the number of + * discarded zeros. Or, if there was no decPt, + * then its just nDigits adjusted by discarded trailing zeros. + */ + if ( decSeen ){ + decExp = decPt - nLeadZero; + } else { + decExp = nDigits+nTrailZero; + } + + /* + * Look for 'e' or 'E' and an optionally signed integer. + */ + if ( (i < l) && (((c = in.charAt(i) )=='e') || (c == 'E') ) ){ + int expSign = 1; + int expVal = 0; + int reallyBig = Integer.MAX_VALUE / 10; + boolean expOverflow = false; + switch( in.charAt(++i) ){ + case '-': + expSign = -1; + //FALLTHROUGH + case '+': + i++; + } + int expAt = i; + expLoop: + while ( i < l ){ + if ( expVal >= reallyBig ){ + // the next character will cause integer + // overflow. + expOverflow = true; + } + switch ( c = in.charAt(i++) ){ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + expVal = expVal*10 + ( (int)c - (int)'0' ); + continue; + default: + i--; // back up. + break expLoop; // stop parsing exponent. + } + } + int expLimit = bigDecimalExponent+nDigits+nTrailZero; + if ( expOverflow || ( expVal > expLimit ) ){ + // + // The intent here is to end up with + // infinity or zero, as appropriate. + // The reason for yielding such a small decExponent, + // rather than something intuitive such as + // expSign*Integer.MAX_VALUE, is that this value + // is subject to further manipulation in + // doubleValue() and floatValue(), and I don't want + // it to be able to cause overflow there! + // (The only way we can get into trouble here is for + // really outrageous nDigits+nTrailZero, such as 2 billion. ) + // + decExp = expSign*expLimit; + } else { + // this should not overflow, since we tested + // for expVal > (MAX+N), where N >= abs(decExp) + decExp = decExp + expSign*expVal; + } + + // if we saw something not a digit ( or end of string ) + // after the [Ee][+-], without seeing any digits at all + // this is certainly an error. If we saw some digits, + // but then some trailing garbage, that might be ok. + // so we just fall through in that case. + // HUMBUG + if ( i == expAt ) + break parseNumber; // certainly bad + } + /* + * We parsed everything we could. + * If there are leftovers, then this is not good input! + */ + if ( i < l && + ((i != l - 1) || + (in.charAt(i) != 'f' && + in.charAt(i) != 'F' && + in.charAt(i) != 'd' && + in.charAt(i) != 'D'))) { + break parseNumber; // go throw exception + } + + return new OldFloatingDecimalForTest( isNegative, decExp, digits, nDigits, false ); + } catch ( StringIndexOutOfBoundsException e ){ } + throw new NumberFormatException("For input string: \"" + in + "\""); + } + + /* + * Take a FloatingDecimal, which we presumably just scanned in, + * and find out what its value is, as a double. + * + * AS A SIDE EFFECT, SET roundDir TO INDICATE PREFERRED + * ROUNDING DIRECTION in case the result is really destined + * for a single-precision float. + */ + + public strictfp double doubleValue(){ + int kDigits = Math.min( nDigits, maxDecimalDigits+1 ); + long lValue; + double dValue; + double rValue, tValue; + + // First, check for NaN and Infinity values + if(digits == infinity || digits == notANumber) { + if(digits == notANumber) + return Double.NaN; + else + return (isNegative?Double.NEGATIVE_INFINITY:Double.POSITIVE_INFINITY); + } + else { + if (mustSetRoundDir) { + roundDir = 0; + } + /* + * convert the lead kDigits to a long integer. + */ + // (special performance hack: start to do it using int) + int iValue = (int)digits[0]-(int)'0'; + int iDigits = Math.min( kDigits, intDecimalDigits ); + for ( int i=1; i < iDigits; i++ ){ + iValue = iValue*10 + (int)digits[i]-(int)'0'; + } + lValue = (long)iValue; + for ( int i=iDigits; i < kDigits; i++ ){ + lValue = lValue*10L + (long)((int)digits[i]-(int)'0'); + } + dValue = (double)lValue; + int exp = decExponent-kDigits; + /* + * lValue now contains a long integer with the value of + * the first kDigits digits of the number. + * dValue contains the (double) of the same. + */ + + if ( nDigits <= maxDecimalDigits ){ + /* + * possibly an easy case. + * We know that the digits can be represented + * exactly. And if the exponent isn't too outrageous, + * the whole thing can be done with one operation, + * thus one rounding error. + * Note that all our constructors trim all leading and + * trailing zeros, so simple values (including zero) + * will always end up here + */ + if (exp == 0 || dValue == 0.0) + return (isNegative)? -dValue : dValue; // small floating integer + else if ( exp >= 0 ){ + if ( exp <= maxSmallTen ){ + /* + * Can get the answer with one operation, + * thus one roundoff. + */ + rValue = dValue * small10pow[exp]; + if ( mustSetRoundDir ){ + tValue = rValue / small10pow[exp]; + roundDir = ( tValue == dValue ) ? 0 + :( tValue < dValue ) ? 1 + : -1; + } + return (isNegative)? -rValue : rValue; + } + int slop = maxDecimalDigits - kDigits; + if ( exp <= maxSmallTen+slop ){ + /* + * We can multiply dValue by 10^(slop) + * and it is still "small" and exact. + * Then we can multiply by 10^(exp-slop) + * with one rounding. + */ + dValue *= small10pow[slop]; + rValue = dValue * small10pow[exp-slop]; + + if ( mustSetRoundDir ){ + tValue = rValue / small10pow[exp-slop]; + roundDir = ( tValue == dValue ) ? 0 + :( tValue < dValue ) ? 1 + : -1; + } + return (isNegative)? -rValue : rValue; + } + /* + * Else we have a hard case with a positive exp. + */ + } else { + if ( exp >= -maxSmallTen ){ + /* + * Can get the answer in one division. + */ + rValue = dValue / small10pow[-exp]; + tValue = rValue * small10pow[-exp]; + if ( mustSetRoundDir ){ + roundDir = ( tValue == dValue ) ? 0 + :( tValue < dValue ) ? 1 + : -1; + } + return (isNegative)? -rValue : rValue; + } + /* + * Else we have a hard case with a negative exp. + */ + } + } + + /* + * Harder cases: + * The sum of digits plus exponent is greater than + * what we think we can do with one error. + * + * Start by approximating the right answer by, + * naively, scaling by powers of 10. + */ + if ( exp > 0 ){ + if ( decExponent > maxDecimalExponent+1 ){ + /* + * Lets face it. This is going to be + * Infinity. Cut to the chase. + */ + return (isNegative)? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + } + if ( (exp&15) != 0 ){ + dValue *= small10pow[exp&15]; + } + if ( (exp>>=4) != 0 ){ + int j; + for( j = 0; exp > 1; j++, exp>>=1 ){ + if ( (exp&1)!=0) + dValue *= big10pow[j]; + } + /* + * The reason for the weird exp > 1 condition + * in the above loop was so that the last multiply + * would get unrolled. We handle it here. + * It could overflow. + */ + double t = dValue * big10pow[j]; + if ( Double.isInfinite( t ) ){ + /* + * It did overflow. + * Look more closely at the result. + * If the exponent is just one too large, + * then use the maximum finite as our estimate + * value. Else call the result infinity + * and punt it. + * ( I presume this could happen because + * rounding forces the result here to be + * an ULP or two larger than + * Double.MAX_VALUE ). + */ + t = dValue / 2.0; + t *= big10pow[j]; + if ( Double.isInfinite( t ) ){ + return (isNegative)? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + } + t = Double.MAX_VALUE; + } + dValue = t; + } + } else if ( exp < 0 ){ + exp = -exp; + if ( decExponent < minDecimalExponent-1 ){ + /* + * Lets face it. This is going to be + * zero. Cut to the chase. + */ + return (isNegative)? -0.0 : 0.0; + } + if ( (exp&15) != 0 ){ + dValue /= small10pow[exp&15]; + } + if ( (exp>>=4) != 0 ){ + int j; + for( j = 0; exp > 1; j++, exp>>=1 ){ + if ( (exp&1)!=0) + dValue *= tiny10pow[j]; + } + /* + * The reason for the weird exp > 1 condition + * in the above loop was so that the last multiply + * would get unrolled. We handle it here. + * It could underflow. + */ + double t = dValue * tiny10pow[j]; + if ( t == 0.0 ){ + /* + * It did underflow. + * Look more closely at the result. + * If the exponent is just one too small, + * then use the minimum finite as our estimate + * value. Else call the result 0.0 + * and punt it. + * ( I presume this could happen because + * rounding forces the result here to be + * an ULP or two less than + * Double.MIN_VALUE ). + */ + t = dValue * 2.0; + t *= tiny10pow[j]; + if ( t == 0.0 ){ + return (isNegative)? -0.0 : 0.0; + } + t = Double.MIN_VALUE; + } + dValue = t; + } + } + + /* + * dValue is now approximately the result. + * The hard part is adjusting it, by comparison + * with OldFDBigIntForTest arithmetic. + * Formulate the EXACT big-number result as + * bigD0 * 10^exp + */ + OldFDBigIntForTest bigD0 = new OldFDBigIntForTest( lValue, digits, kDigits, nDigits ); + exp = decExponent - nDigits; + + correctionLoop: + while(true){ + /* AS A SIDE EFFECT, THIS METHOD WILL SET THE INSTANCE VARIABLES + * bigIntExp and bigIntNBits + */ + OldFDBigIntForTest bigB = doubleToBigInt( dValue ); + + /* + * Scale bigD, bigB appropriately for + * big-integer operations. + * Naively, we multiply by powers of ten + * and powers of two. What we actually do + * is keep track of the powers of 5 and + * powers of 2 we would use, then factor out + * common divisors before doing the work. + */ + int B2, B5; // powers of 2, 5 in bigB + int D2, D5; // powers of 2, 5 in bigD + int Ulp2; // powers of 2 in halfUlp. + if ( exp >= 0 ){ + B2 = B5 = 0; + D2 = D5 = exp; + } else { + B2 = B5 = -exp; + D2 = D5 = 0; + } + if ( bigIntExp >= 0 ){ + B2 += bigIntExp; + } else { + D2 -= bigIntExp; + } + Ulp2 = B2; + // shift bigB and bigD left by a number s. t. + // halfUlp is still an integer. + int hulpbias; + if ( bigIntExp+bigIntNBits <= -expBias+1 ){ + // This is going to be a denormalized number + // (if not actually zero). + // half an ULP is at 2^-(expBias+expShift+1) + hulpbias = bigIntExp+ expBias + expShift; + } else { + hulpbias = expShift + 2 - bigIntNBits; + } + B2 += hulpbias; + D2 += hulpbias; + // if there are common factors of 2, we might just as well + // factor them out, as they add nothing useful. + int common2 = Math.min( B2, Math.min( D2, Ulp2 ) ); + B2 -= common2; + D2 -= common2; + Ulp2 -= common2; + // do multiplications by powers of 5 and 2 + bigB = multPow52( bigB, B5, B2 ); + OldFDBigIntForTest bigD = multPow52( new OldFDBigIntForTest( bigD0 ), D5, D2 ); + // + // to recap: + // bigB is the scaled-big-int version of our floating-point + // candidate. + // bigD is the scaled-big-int version of the exact value + // as we understand it. + // halfUlp is 1/2 an ulp of bigB, except for special cases + // of exact powers of 2 + // + // the plan is to compare bigB with bigD, and if the difference + // is less than halfUlp, then we're satisfied. Otherwise, + // use the ratio of difference to halfUlp to calculate a fudge + // factor to add to the floating value, then go 'round again. + // + OldFDBigIntForTest diff; + int cmpResult; + boolean overvalue; + if ( (cmpResult = bigB.cmp( bigD ) ) > 0 ){ + overvalue = true; // our candidate is too big. + diff = bigB.sub( bigD ); + if ( (bigIntNBits == 1) && (bigIntExp > -expBias+1) ){ + // candidate is a normalized exact power of 2 and + // is too big. We will be subtracting. + // For our purposes, ulp is the ulp of the + // next smaller range. + Ulp2 -= 1; + if ( Ulp2 < 0 ){ + // rats. Cannot de-scale ulp this far. + // must scale diff in other direction. + Ulp2 = 0; + diff.lshiftMe( 1 ); + } + } + } else if ( cmpResult < 0 ){ + overvalue = false; // our candidate is too small. + diff = bigD.sub( bigB ); + } else { + // the candidate is exactly right! + // this happens with surprising frequency + break correctionLoop; + } + OldFDBigIntForTest halfUlp = constructPow52( B5, Ulp2 ); + if ( (cmpResult = diff.cmp( halfUlp ) ) < 0 ){ + // difference is small. + // this is close enough + if (mustSetRoundDir) { + roundDir = overvalue ? -1 : 1; + } + break correctionLoop; + } else if ( cmpResult == 0 ){ + // difference is exactly half an ULP + // round to some other value maybe, then finish + dValue += 0.5*ulp( dValue, overvalue ); + // should check for bigIntNBits == 1 here?? + if (mustSetRoundDir) { + roundDir = overvalue ? -1 : 1; + } + break correctionLoop; + } else { + // difference is non-trivial. + // could scale addend by ratio of difference to + // halfUlp here, if we bothered to compute that difference. + // Most of the time ( I hope ) it is about 1 anyway. + dValue += ulp( dValue, overvalue ); + if ( dValue == 0.0 || dValue == Double.POSITIVE_INFINITY ) + break correctionLoop; // oops. Fell off end of range. + continue; // try again. + } + + } + return (isNegative)? -dValue : dValue; + } + } + + /* + * Take a FloatingDecimal, which we presumably just scanned in, + * and find out what its value is, as a float. + * This is distinct from doubleValue() to avoid the extremely + * unlikely case of a double rounding error, wherein the conversion + * to double has one rounding error, and the conversion of that double + * to a float has another rounding error, IN THE WRONG DIRECTION, + * ( because of the preference to a zero low-order bit ). + */ + + public strictfp float floatValue(){ + int kDigits = Math.min( nDigits, singleMaxDecimalDigits+1 ); + int iValue; + float fValue; + + // First, check for NaN and Infinity values + if(digits == infinity || digits == notANumber) { + if(digits == notANumber) + return Float.NaN; + else + return (isNegative?Float.NEGATIVE_INFINITY:Float.POSITIVE_INFINITY); + } + else { + /* + * convert the lead kDigits to an integer. + */ + iValue = (int)digits[0]-(int)'0'; + for ( int i=1; i < kDigits; i++ ){ + iValue = iValue*10 + (int)digits[i]-(int)'0'; + } + fValue = (float)iValue; + int exp = decExponent-kDigits; + /* + * iValue now contains an integer with the value of + * the first kDigits digits of the number. + * fValue contains the (float) of the same. + */ + + if ( nDigits <= singleMaxDecimalDigits ){ + /* + * possibly an easy case. + * We know that the digits can be represented + * exactly. And if the exponent isn't too outrageous, + * the whole thing can be done with one operation, + * thus one rounding error. + * Note that all our constructors trim all leading and + * trailing zeros, so simple values (including zero) + * will always end up here. + */ + if (exp == 0 || fValue == 0.0f) + return (isNegative)? -fValue : fValue; // small floating integer + else if ( exp >= 0 ){ + if ( exp <= singleMaxSmallTen ){ + /* + * Can get the answer with one operation, + * thus one roundoff. + */ + fValue *= singleSmall10pow[exp]; + return (isNegative)? -fValue : fValue; + } + int slop = singleMaxDecimalDigits - kDigits; + if ( exp <= singleMaxSmallTen+slop ){ + /* + * We can multiply dValue by 10^(slop) + * and it is still "small" and exact. + * Then we can multiply by 10^(exp-slop) + * with one rounding. + */ + fValue *= singleSmall10pow[slop]; + fValue *= singleSmall10pow[exp-slop]; + return (isNegative)? -fValue : fValue; + } + /* + * Else we have a hard case with a positive exp. + */ + } else { + if ( exp >= -singleMaxSmallTen ){ + /* + * Can get the answer in one division. + */ + fValue /= singleSmall10pow[-exp]; + return (isNegative)? -fValue : fValue; + } + /* + * Else we have a hard case with a negative exp. + */ + } + } else if ( (decExponent >= nDigits) && (nDigits+decExponent <= maxDecimalDigits) ){ + /* + * In double-precision, this is an exact floating integer. + * So we can compute to double, then shorten to float + * with one round, and get the right answer. + * + * First, finish accumulating digits. + * Then convert that integer to a double, multiply + * by the appropriate power of ten, and convert to float. + */ + long lValue = (long)iValue; + for ( int i=kDigits; i < nDigits; i++ ){ + lValue = lValue*10L + (long)((int)digits[i]-(int)'0'); + } + double dValue = (double)lValue; + exp = decExponent-nDigits; + dValue *= small10pow[exp]; + fValue = (float)dValue; + return (isNegative)? -fValue : fValue; + + } + /* + * Harder cases: + * The sum of digits plus exponent is greater than + * what we think we can do with one error. + * + * Start by weeding out obviously out-of-range + * results, then convert to double and go to + * common hard-case code. + */ + if ( decExponent > singleMaxDecimalExponent+1 ){ + /* + * Lets face it. This is going to be + * Infinity. Cut to the chase. + */ + return (isNegative)? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; + } else if ( decExponent < singleMinDecimalExponent-1 ){ + /* + * Lets face it. This is going to be + * zero. Cut to the chase. + */ + return (isNegative)? -0.0f : 0.0f; + } + + /* + * Here, we do 'way too much work, but throwing away + * our partial results, and going and doing the whole + * thing as double, then throwing away half the bits that computes + * when we convert back to float. + * + * The alternative is to reproduce the whole multiple-precision + * algorithm for float precision, or to try to parameterize it + * for common usage. The former will take about 400 lines of code, + * and the latter I tried without success. Thus the semi-hack + * answer here. + */ + mustSetRoundDir = !fromHex; + double dValue = doubleValue(); + return stickyRound( dValue ); + } + } + + + /* + * All the positive powers of 10 that can be + * represented exactly in double/float. + */ + private static final double small10pow[] = { + 1.0e0, + 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, + 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10, + 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, + 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20, + 1.0e21, 1.0e22 + }; + + private static final float singleSmall10pow[] = { + 1.0e0f, + 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f, + 1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f + }; + + private static final double big10pow[] = { + 1e16, 1e32, 1e64, 1e128, 1e256 }; + private static final double tiny10pow[] = { + 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; + + private static final int maxSmallTen = small10pow.length-1; + private static final int singleMaxSmallTen = singleSmall10pow.length-1; + + private static final int small5pow[] = { + 1, + 5, + 5*5, + 5*5*5, + 5*5*5*5, + 5*5*5*5*5, + 5*5*5*5*5*5, + 5*5*5*5*5*5*5, + 5*5*5*5*5*5*5*5, + 5*5*5*5*5*5*5*5*5, + 5*5*5*5*5*5*5*5*5*5, + 5*5*5*5*5*5*5*5*5*5*5, + 5*5*5*5*5*5*5*5*5*5*5*5, + 5*5*5*5*5*5*5*5*5*5*5*5*5 + }; + + + private static final long long5pow[] = { + 1L, + 5L, + 5L*5, + 5L*5*5, + 5L*5*5*5, + 5L*5*5*5*5, + 5L*5*5*5*5*5, + 5L*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, + }; + + // approximately ceil( log2( long5pow[i] ) ) + private static final int n5bits[] = { + 0, + 3, + 5, + 7, + 10, + 12, + 14, + 17, + 19, + 21, + 24, + 26, + 28, + 31, + 33, + 35, + 38, + 40, + 42, + 45, + 47, + 49, + 52, + 54, + 56, + 59, + 61, + }; + + private static final char infinity[] = { 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y' }; + private static final char notANumber[] = { 'N', 'a', 'N' }; + private static final char zero[] = { '0', '0', '0', '0', '0', '0', '0', '0' }; + + + /* + * Grammar is compatible with hexadecimal floating-point constants + * described in section 6.4.4.2 of the C99 specification. + */ + private static Pattern hexFloatPattern = null; + private static synchronized Pattern getHexFloatPattern() { + if (hexFloatPattern == null) { + hexFloatPattern = Pattern.compile( + //1 234 56 7 8 9 + "([-+])?0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?" + ); + } + return hexFloatPattern; + } + + /* + * Convert string s to a suitable floating decimal; uses the + * double constructor and set the roundDir variable appropriately + * in case the value is later converted to a float. + */ + static OldFloatingDecimalForTest parseHexString(String s) { + // Verify string is a member of the hexadecimal floating-point + // string language. + Matcher m = getHexFloatPattern().matcher(s); + boolean validInput = m.matches(); + + if (!validInput) { + // Input does not match pattern + throw new NumberFormatException("For input string: \"" + s + "\""); + } else { // validInput + /* + * We must isolate the sign, significand, and exponent + * fields. The sign value is straightforward. Since + * floating-point numbers are stored with a normalized + * representation, the significand and exponent are + * interrelated. + * + * After extracting the sign, we normalized the + * significand as a hexadecimal value, calculating an + * exponent adjust for any shifts made during + * normalization. If the significand is zero, the + * exponent doesn't need to be examined since the output + * will be zero. + * + * Next the exponent in the input string is extracted. + * Afterwards, the significand is normalized as a *binary* + * value and the input value's normalized exponent can be + * computed. The significand bits are copied into a + * double significand; if the string has more logical bits + * than can fit in a double, the extra bits affect the + * round and sticky bits which are used to round the final + * value. + */ + + // Extract significand sign + String group1 = m.group(1); + double sign = (( group1 == null ) || group1.equals("+"))? 1.0 : -1.0; + + + // Extract Significand magnitude + /* + * Based on the form of the significand, calculate how the + * binary exponent needs to be adjusted to create a + * normalized *hexadecimal* floating-point number; that + * is, a number where there is one nonzero hex digit to + * the left of the (hexa)decimal point. Since we are + * adjusting a binary, not hexadecimal exponent, the + * exponent is adjusted by a multiple of 4. + * + * There are a number of significand scenarios to consider; + * letters are used in indicate nonzero digits: + * + * 1. 000xxxx => x.xxx normalized + * increase exponent by (number of x's - 1)*4 + * + * 2. 000xxx.yyyy => x.xxyyyy normalized + * increase exponent by (number of x's - 1)*4 + * + * 3. .000yyy => y.yy normalized + * decrease exponent by (number of zeros + 1)*4 + * + * 4. 000.00000yyy => y.yy normalized + * decrease exponent by (number of zeros to right of point + 1)*4 + * + * If the significand is exactly zero, return a properly + * signed zero. + */ + + String significandString =null; + int signifLength = 0; + int exponentAdjust = 0; + { + int leftDigits = 0; // number of meaningful digits to + // left of "decimal" point + // (leading zeros stripped) + int rightDigits = 0; // number of digits to right of + // "decimal" point; leading zeros + // must always be accounted for + /* + * The significand is made up of either + * + * 1. group 4 entirely (integer portion only) + * + * OR + * + * 2. the fractional portion from group 7 plus any + * (optional) integer portions from group 6. + */ + String group4; + if( (group4 = m.group(4)) != null) { // Integer-only significand + // Leading zeros never matter on the integer portion + significandString = stripLeadingZeros(group4); + leftDigits = significandString.length(); + } + else { + // Group 6 is the optional integer; leading zeros + // never matter on the integer portion + String group6 = stripLeadingZeros(m.group(6)); + leftDigits = group6.length(); + + // fraction + String group7 = m.group(7); + rightDigits = group7.length(); + + // Turn "integer.fraction" into "integer"+"fraction" + significandString = + ((group6 == null)?"":group6) + // is the null + // check necessary? + group7; + } + + significandString = stripLeadingZeros(significandString); + signifLength = significandString.length(); + + /* + * Adjust exponent as described above + */ + if (leftDigits >= 1) { // Cases 1 and 2 + exponentAdjust = 4*(leftDigits - 1); + } else { // Cases 3 and 4 + exponentAdjust = -4*( rightDigits - signifLength + 1); + } + + // If the significand is zero, the exponent doesn't + // matter; return a properly signed zero. + + if (signifLength == 0) { // Only zeros in input + return new OldFloatingDecimalForTest(sign * 0.0); + } + } + + // Extract Exponent + /* + * Use an int to read in the exponent value; this should + * provide more than sufficient range for non-contrived + * inputs. If reading the exponent in as an int does + * overflow, examine the sign of the exponent and + * significand to determine what to do. + */ + String group8 = m.group(8); + boolean positiveExponent = ( group8 == null ) || group8.equals("+"); + long unsignedRawExponent; + try { + unsignedRawExponent = Integer.parseInt(m.group(9)); + } + catch (NumberFormatException e) { + // At this point, we know the exponent is + // syntactically well-formed as a sequence of + // digits. Therefore, if an NumberFormatException + // is thrown, it must be due to overflowing int's + // range. Also, at this point, we have already + // checked for a zero significand. Thus the signs + // of the exponent and significand determine the + // final result: + // + // significand + // + - + // exponent + +infinity -infinity + // - +0.0 -0.0 + return new OldFloatingDecimalForTest(sign * (positiveExponent ? + Double.POSITIVE_INFINITY : 0.0)); + } + + long rawExponent = + (positiveExponent ? 1L : -1L) * // exponent sign + unsignedRawExponent; // exponent magnitude + + // Calculate partially adjusted exponent + long exponent = rawExponent + exponentAdjust ; + + // Starting copying non-zero bits into proper position in + // a long; copy explicit bit too; this will be masked + // later for normal values. + + boolean round = false; + boolean sticky = false; + int bitsCopied=0; + int nextShift=0; + long significand=0L; + // First iteration is different, since we only copy + // from the leading significand bit; one more exponent + // adjust will be needed... + + // IMPORTANT: make leadingDigit a long to avoid + // surprising shift semantics! + long leadingDigit = getHexDigit(significandString, 0); + + /* + * Left shift the leading digit (53 - (bit position of + * leading 1 in digit)); this sets the top bit of the + * significand to 1. The nextShift value is adjusted + * to take into account the number of bit positions of + * the leadingDigit actually used. Finally, the + * exponent is adjusted to normalize the significand + * as a binary value, not just a hex value. + */ + if (leadingDigit == 1) { + significand |= leadingDigit << 52; + nextShift = 52 - 4; + /* exponent += 0 */ } + else if (leadingDigit <= 3) { // [2, 3] + significand |= leadingDigit << 51; + nextShift = 52 - 5; + exponent += 1; + } + else if (leadingDigit <= 7) { // [4, 7] + significand |= leadingDigit << 50; + nextShift = 52 - 6; + exponent += 2; + } + else if (leadingDigit <= 15) { // [8, f] + significand |= leadingDigit << 49; + nextShift = 52 - 7; + exponent += 3; + } else { + throw new AssertionError("Result from digit conversion too large!"); + } + // The preceding if-else could be replaced by a single + // code block based on the high-order bit set in + // leadingDigit. Given leadingOnePosition, + + // significand |= leadingDigit << (SIGNIFICAND_WIDTH - leadingOnePosition); + // nextShift = 52 - (3 + leadingOnePosition); + // exponent += (leadingOnePosition-1); + + + /* + * Now the exponent variable is equal to the normalized + * binary exponent. Code below will make representation + * adjustments if the exponent is incremented after + * rounding (includes overflows to infinity) or if the + * result is subnormal. + */ + + // Copy digit into significand until the significand can't + // hold another full hex digit or there are no more input + // hex digits. + int i = 0; + for(i = 1; + i < signifLength && nextShift >= 0; + i++) { + long currentDigit = getHexDigit(significandString, i); + significand |= (currentDigit << nextShift); + nextShift-=4; + } + + // After the above loop, the bulk of the string is copied. + // Now, we must copy any partial hex digits into the + // significand AND compute the round bit and start computing + // sticky bit. + + if ( i < signifLength ) { // at least one hex input digit exists + long currentDigit = getHexDigit(significandString, i); + + // from nextShift, figure out how many bits need + // to be copied, if any + switch(nextShift) { // must be negative + case -1: + // three bits need to be copied in; can + // set round bit + significand |= ((currentDigit & 0xEL) >> 1); + round = (currentDigit & 0x1L) != 0L; + break; + + case -2: + // two bits need to be copied in; can + // set round and start sticky + significand |= ((currentDigit & 0xCL) >> 2); + round = (currentDigit &0x2L) != 0L; + sticky = (currentDigit & 0x1L) != 0; + break; + + case -3: + // one bit needs to be copied in + significand |= ((currentDigit & 0x8L)>>3); + // Now set round and start sticky, if possible + round = (currentDigit &0x4L) != 0L; + sticky = (currentDigit & 0x3L) != 0; + break; + + case -4: + // all bits copied into significand; set + // round and start sticky + round = ((currentDigit & 0x8L) != 0); // is top bit set? + // nonzeros in three low order bits? + sticky = (currentDigit & 0x7L) != 0; + break; + + default: + throw new AssertionError("Unexpected shift distance remainder."); + // break; + } + + // Round is set; sticky might be set. + + // For the sticky bit, it suffices to check the + // current digit and test for any nonzero digits in + // the remaining unprocessed input. + i++; + while(i < signifLength && !sticky) { + currentDigit = getHexDigit(significandString,i); + sticky = sticky || (currentDigit != 0); + i++; + } + + } + // else all of string was seen, round and sticky are + // correct as false. + + + // Check for overflow and update exponent accordingly. + + if (exponent > Double.MAX_EXPONENT) { // Infinite result + // overflow to properly signed infinity + return new OldFloatingDecimalForTest(sign * Double.POSITIVE_INFINITY); + } else { // Finite return value + if (exponent <= Double.MAX_EXPONENT && // (Usually) normal result + exponent >= Double.MIN_EXPONENT) { + + // The result returned in this block cannot be a + // zero or subnormal; however after the + // significand is adjusted from rounding, we could + // still overflow in infinity. + + // AND exponent bits into significand; if the + // significand is incremented and overflows from + // rounding, this combination will update the + // exponent correctly, even in the case of + // Double.MAX_VALUE overflowing to infinity. + + significand = (( (exponent + + (long)DoubleConsts.EXP_BIAS) << + (DoubleConsts.SIGNIFICAND_WIDTH-1)) + & DoubleConsts.EXP_BIT_MASK) | + (DoubleConsts.SIGNIF_BIT_MASK & significand); + + } else { // Subnormal or zero + // (exponent < Double.MIN_EXPONENT) + + if (exponent < (DoubleConsts.MIN_SUB_EXPONENT -1 )) { + // No way to round back to nonzero value + // regardless of significand if the exponent is + // less than -1075. + return new OldFloatingDecimalForTest(sign * 0.0); + } else { // -1075 <= exponent <= MIN_EXPONENT -1 = -1023 + /* + * Find bit position to round to; recompute + * round and sticky bits, and shift + * significand right appropriately. + */ + + sticky = sticky || round; + round = false; + + // Number of bits of significand to preserve is + // exponent - abs_min_exp +1 + // check: + // -1075 +1074 + 1 = 0 + // -1023 +1074 + 1 = 52 + + int bitsDiscarded = 53 - + ((int)exponent - DoubleConsts.MIN_SUB_EXPONENT + 1); + assert bitsDiscarded >= 1 && bitsDiscarded <= 53; + + // What to do here: + // First, isolate the new round bit + round = (significand & (1L << (bitsDiscarded -1))) != 0L; + if (bitsDiscarded > 1) { + // create mask to update sticky bits; low + // order bitsDiscarded bits should be 1 + long mask = ~((~0L) << (bitsDiscarded -1)); + sticky = sticky || ((significand & mask) != 0L ) ; + } + + // Now, discard the bits + significand = significand >> bitsDiscarded; + + significand = (( ((long)(Double.MIN_EXPONENT -1) + // subnorm exp. + (long)DoubleConsts.EXP_BIAS) << + (DoubleConsts.SIGNIFICAND_WIDTH-1)) + & DoubleConsts.EXP_BIT_MASK) | + (DoubleConsts.SIGNIF_BIT_MASK & significand); + } + } + + // The significand variable now contains the currently + // appropriate exponent bits too. + + /* + * Determine if significand should be incremented; + * making this determination depends on the least + * significant bit and the round and sticky bits. + * + * Round to nearest even rounding table, adapted from + * table 4.7 in "Computer Arithmetic" by IsraelKoren. + * The digit to the left of the "decimal" point is the + * least significant bit, the digits to the right of + * the point are the round and sticky bits + * + * Number Round(x) + * x0.00 x0. + * x0.01 x0. + * x0.10 x0. + * x0.11 x1. = x0. +1 + * x1.00 x1. + * x1.01 x1. + * x1.10 x1. + 1 + * x1.11 x1. + 1 + */ + boolean incremented = false; + boolean leastZero = ((significand & 1L) == 0L); + if( ( leastZero && round && sticky ) || + ((!leastZero) && round )) { + incremented = true; + significand++; + } + + OldFloatingDecimalForTest fd = new OldFloatingDecimalForTest(Math.copySign( + Double.longBitsToDouble(significand), + sign)); + + /* + * Set roundingDir variable field of fd properly so + * that the input string can be properly rounded to a + * float value. There are two cases to consider: + * + * 1. rounding to double discards sticky bit + * information that would change the result of a float + * rounding (near halfway case between two floats) + * + * 2. rounding to double rounds up when rounding up + * would not occur when rounding to float. + * + * For former case only needs to be considered when + * the bits rounded away when casting to float are all + * zero; otherwise, float round bit is properly set + * and sticky will already be true. + * + * The lower exponent bound for the code below is the + * minimum (normalized) subnormal exponent - 1 since a + * value with that exponent can round up to the + * minimum subnormal value and the sticky bit + * information must be preserved (i.e. case 1). + */ + if ((exponent >= FloatConsts.MIN_SUB_EXPONENT-1) && + (exponent <= Float.MAX_EXPONENT ) ){ + // Outside above exponent range, the float value + // will be zero or infinity. + + /* + * If the low-order 28 bits of a rounded double + * significand are 0, the double could be a + * half-way case for a rounding to float. If the + * double value is a half-way case, the double + * significand may have to be modified to round + * the the right float value (see the stickyRound + * method). If the rounding to double has lost + * what would be float sticky bit information, the + * double significand must be incremented. If the + * double value's significand was itself + * incremented, the float value may end up too + * large so the increment should be undone. + */ + if ((significand & 0xfffffffL) == 0x0L) { + // For negative values, the sign of the + // roundDir is the same as for positive values + // since adding 1 increasing the significand's + // magnitude and subtracting 1 decreases the + // significand's magnitude. If neither round + // nor sticky is true, the double value is + // exact and no adjustment is required for a + // proper float rounding. + if( round || sticky) { + if (leastZero) { // prerounding lsb is 0 + // If round and sticky were both true, + // and the least significant + // significand bit were 0, the rounded + // significand would not have its + // low-order bits be zero. Therefore, + // we only need to adjust the + // significand if round XOR sticky is + // true. + if (round ^ sticky) { + fd.roundDir = 1; + } + } + else { // prerounding lsb is 1 + // If the prerounding lsb is 1 and the + // resulting significand has its + // low-order bits zero, the significand + // was incremented. Here, we undo the + // increment, which will ensure the + // right guard and sticky bits for the + // float rounding. + if (round) + fd.roundDir = -1; + } + } + } + } + + fd.fromHex = true; + return fd; + } + } + } + + /** + * Return s with any leading zeros removed. + */ + static String stripLeadingZeros(String s) { + return s.replaceFirst("^0+", ""); + } + + /** + * Extract a hexadecimal digit from position position + * of string s. + */ + static int getHexDigit(String s, int position) { + int value = Character.digit(s.charAt(position), 16); + if (value <= -1 || value >= 16) { + throw new AssertionError("Unexpected failure of digit conversion of " + + s.charAt(position)); + } + return value; + } + + +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/jdk/internal/math/FloatingDecimal/TestFDBigInteger.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/jdk/internal/math/FloatingDecimal/TestFDBigInteger.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,435 @@ +/* + * 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.math.BigInteger; +import java.util.Random; +import jdk.internal.math.FDBigInteger; + +/** + * @test + * @bug 7032154 + * @summary unit testys of FDBigInteger + * @modules java.base/jdk.internal.math + * @author Dmitry Nadezhin + */ +public class TestFDBigInteger { + + private static final int MAX_P5 = 413; + private static final int MAX_P2 = 65; + private static final long LONG_SIGN_MASK = (1L << 63); + private static final BigInteger FIVE = BigInteger.valueOf(5); + private static final FDBigInteger MUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0)); + private static final FDBigInteger IMMUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0)); + private static final FDBigInteger IMMUTABLE_MILLION = genMillion1(); + private static final FDBigInteger IMMUTABLE_BILLION = genBillion1(); + private static final FDBigInteger IMMUTABLE_TEN18 = genTen18(); + + static { + IMMUTABLE_ZERO.makeImmutable(); + IMMUTABLE_MILLION.makeImmutable(); + IMMUTABLE_BILLION.makeImmutable(); + IMMUTABLE_TEN18.makeImmutable(); + } + + private static FDBigInteger mutable(String hex, int offset) { + char[] chars = new BigInteger(hex, 16).toString().toCharArray(); + return new FDBigInteger(0, chars, 0, chars.length).multByPow52(0, offset * 32); + } + + private static FDBigInteger immutable(String hex, int offset) { + FDBigInteger fd = mutable(hex, offset); + fd.makeImmutable(); + return fd; + } + + private static BigInteger biPow52(int p5, int p2) { + return FIVE.pow(p5).shiftLeft(p2); + } + + // data.length == 1, nWords == 1, offset == 0 + private static FDBigInteger genMillion1() { + return FDBigInteger.valueOfPow52(6, 0).leftShift(6); + } + + // data.length == 2, nWords == 1, offset == 0 + private static FDBigInteger genMillion2() { + return FDBigInteger.valueOfMulPow52(1000000L, 0, 0); + } + + // data.length == 1, nWords == 1, offset == 0 + private static FDBigInteger genBillion1() { + return FDBigInteger.valueOfPow52(9, 0).leftShift(9); + } + + // data.length == 2, nWords == 2, offset == 0 + private static FDBigInteger genTen18() { + return FDBigInteger.valueOfPow52(18, 0).leftShift(18); + } + + private static void check(BigInteger expected, FDBigInteger actual, String message) throws Exception { + if (!expected.equals(actual.toBigInteger())) { + throw new Exception(message + " result " + actual.toHexString() + " expected " + expected.toString(16)); + } + } + + private static void testValueOfPow52(int p5, int p2) throws Exception { + check(biPow52(p5, p2), FDBigInteger.valueOfPow52(p5, p2), + "valueOfPow52(" + p5 + "," + p2 + ")"); + } + + private static void testValueOfPow52() throws Exception { + for (int p5 = 0; p5 <= MAX_P5; p5++) { + for (int p2 = 0; p2 <= MAX_P2; p2++) { + testValueOfPow52(p5, p2); + } + } + } + + private static void testValueOfMulPow52(long value, int p5, int p2) throws Exception { + BigInteger bi = BigInteger.valueOf(value & ~LONG_SIGN_MASK); + if (value < 0) { + bi = bi.setBit(63); + } + check(biPow52(p5, p2).multiply(bi), FDBigInteger.valueOfMulPow52(value, p5, p2), + "valueOfMulPow52(" + Long.toHexString(value) + "." + p5 + "," + p2 + ")"); + } + + private static void testValueOfMulPow52(long value, int p5) throws Exception { + testValueOfMulPow52(value, p5, 0); + testValueOfMulPow52(value, p5, 1); + testValueOfMulPow52(value, p5, 30); + testValueOfMulPow52(value, p5, 31); + testValueOfMulPow52(value, p5, 33); + testValueOfMulPow52(value, p5, 63); + } + + private static void testValueOfMulPow52() throws Exception { + for (int p5 = 0; p5 <= MAX_P5; p5++) { + testValueOfMulPow52(0xFFFFFFFFL, p5); + testValueOfMulPow52(0x123456789AL, p5); + testValueOfMulPow52(0x7FFFFFFFFFFFFFFFL, p5); + testValueOfMulPow52(0xFFFFFFFFFFF54321L, p5); + } + } + + private static void testLeftShift(FDBigInteger t, int shift, boolean isImmutable) throws Exception { + BigInteger bt = t.toBigInteger(); + FDBigInteger r = t.leftShift(shift); + if ((bt.signum() == 0 || shift == 0 || !isImmutable) && r != t) { + throw new Exception("leftShift doesn't reuse its argument"); + } + if (isImmutable) { + check(bt, t, "leftShift corrupts its argument"); + } + check(bt.shiftLeft(shift), r, "leftShift returns wrong result"); + } + + private static void testLeftShift() throws Exception { + testLeftShift(IMMUTABLE_ZERO, 0, true); + testLeftShift(IMMUTABLE_ZERO, 10, true); + testLeftShift(MUTABLE_ZERO, 0, false); + testLeftShift(MUTABLE_ZERO, 10, false); + + testLeftShift(IMMUTABLE_MILLION, 0, true); + testLeftShift(IMMUTABLE_MILLION, 1, true); + testLeftShift(IMMUTABLE_MILLION, 12, true); + testLeftShift(IMMUTABLE_MILLION, 13, true); + testLeftShift(IMMUTABLE_MILLION, 32, true); + testLeftShift(IMMUTABLE_MILLION, 33, true); + testLeftShift(IMMUTABLE_MILLION, 44, true); + testLeftShift(IMMUTABLE_MILLION, 45, true); + + testLeftShift(genMillion1(), 0, false); + testLeftShift(genMillion1(), 1, false); + testLeftShift(genMillion1(), 12, false); + testLeftShift(genMillion1(), 13, false); + testLeftShift(genMillion1(), 25, false); + testLeftShift(genMillion1(), 26, false); + testLeftShift(genMillion1(), 32, false); + testLeftShift(genMillion1(), 33, false); + testLeftShift(genMillion1(), 44, false); + testLeftShift(genMillion1(), 45, false); + + testLeftShift(genMillion2(), 0, false); + testLeftShift(genMillion2(), 1, false); + testLeftShift(genMillion2(), 12, false); + testLeftShift(genMillion2(), 13, false); + testLeftShift(genMillion2(), 25, false); + testLeftShift(genMillion2(), 26, false); + testLeftShift(genMillion2(), 32, false); + testLeftShift(genMillion2(), 33, false); + testLeftShift(genMillion2(), 44, false); + testLeftShift(genMillion2(), 45, false); + } + + private static void testQuoRemIteration(FDBigInteger t, FDBigInteger s) throws Exception { + BigInteger bt = t.toBigInteger(); + BigInteger bs = s.toBigInteger(); + int q = t.quoRemIteration(s); + BigInteger[] qr = bt.divideAndRemainder(bs); + if (!BigInteger.valueOf(q).equals(qr[0])) { + throw new Exception("quoRemIteration returns incorrect quo"); + } + check(qr[1].multiply(BigInteger.TEN), t, "quoRemIteration returns incorrect rem"); + } + + private static void testQuoRemIteration() throws Exception { + // IMMUTABLE_TEN18 == 0de0b6b3a7640000 + // q = 0 + testQuoRemIteration(mutable("00000001", 0), IMMUTABLE_TEN18); + testQuoRemIteration(mutable("00000001", 1), IMMUTABLE_TEN18); + testQuoRemIteration(mutable("0de0b6b2", 1), IMMUTABLE_TEN18); + // q = 1 -> q = 0 + testQuoRemIteration(mutable("0de0b6b3", 1), IMMUTABLE_TEN18); + testQuoRemIteration(mutable("0de0b6b3a763FFFF", 0), IMMUTABLE_TEN18); + // q = 1 + testQuoRemIteration(mutable("0de0b6b3a7640000", 0), IMMUTABLE_TEN18); + testQuoRemIteration(mutable("0de0b6b3FFFFFFFF", 0), IMMUTABLE_TEN18); + testQuoRemIteration(mutable("8ac72304", 1), IMMUTABLE_TEN18); + testQuoRemIteration(mutable("0de0b6b400000000", 0), IMMUTABLE_TEN18); + testQuoRemIteration(mutable("8ac72305", 1), IMMUTABLE_TEN18); + // q = 18 + testQuoRemIteration(mutable("FFFFFFFF", 1), IMMUTABLE_TEN18); + } + + private static void testCmp(FDBigInteger t, FDBigInteger o) throws Exception { + BigInteger bt = t.toBigInteger(); + BigInteger bo = o.toBigInteger(); + int cmp = t.cmp(o); + int bcmp = bt.compareTo(bo); + if (bcmp != cmp) { + throw new Exception("cmp returns " + cmp + " expected " + bcmp); + } + check(bt, t, "cmp corrupts this"); + check(bo, o, "cmp corrupts other"); + if (o.cmp(t) != -cmp) { + throw new Exception("asymmetrical cmp"); + } + check(bt, t, "cmp corrupts this"); + check(bo, o, "cmp corrupts other"); + } + + private static void testCmp() throws Exception { + testCmp(mutable("FFFFFFFF", 0), mutable("100000000", 0)); + testCmp(mutable("FFFFFFFF", 0), mutable("1", 1)); + testCmp(mutable("5", 0), mutable("6", 0)); + testCmp(mutable("5", 0), mutable("5", 0)); + testCmp(mutable("5000000001", 0), mutable("500000001", 0)); + testCmp(mutable("5000000001", 0), mutable("6", 1)); + testCmp(mutable("5000000001", 0), mutable("5", 1)); + testCmp(mutable("5000000000", 0), mutable("5", 1)); + } + + private static void testCmpPow52(FDBigInteger t, int p5, int p2) throws Exception { + FDBigInteger o = FDBigInteger.valueOfPow52(p5, p2); + BigInteger bt = t.toBigInteger(); + BigInteger bo = biPow52(p5, p2); + int cmp = t.cmp(o); + int bcmp = bt.compareTo(bo); + if (bcmp != cmp) { + throw new Exception("cmpPow52 returns " + cmp + " expected " + bcmp); + } + check(bt, t, "cmpPow52 corrupts this"); + check(bo, o, "cmpPow5 corrupts other"); + } + + private static void testCmpPow52() throws Exception { + testCmpPow52(mutable("00000002", 1), 0, 31); + testCmpPow52(mutable("00000002", 1), 0, 32); + testCmpPow52(mutable("00000002", 1), 0, 33); + testCmpPow52(mutable("00000002", 1), 0, 34); + testCmpPow52(mutable("00000002", 1), 0, 64); + testCmpPow52(mutable("00000003", 1), 0, 32); + testCmpPow52(mutable("00000003", 1), 0, 33); + testCmpPow52(mutable("00000003", 1), 0, 34); + } + + private static void testAddAndCmp(FDBigInteger t, FDBigInteger x, FDBigInteger y) throws Exception { + BigInteger bt = t.toBigInteger(); + BigInteger bx = x.toBigInteger(); + BigInteger by = y.toBigInteger(); + int cmp = t.addAndCmp(x, y); + int bcmp = bt.compareTo(bx.add(by)); + if (bcmp != cmp) { + throw new Exception("addAndCmp returns " + cmp + " expected " + bcmp); + } + check(bt, t, "addAndCmp corrupts this"); + check(bx, x, "addAndCmp corrupts x"); + check(by, y, "addAndCmp corrupts y"); + } + + private static void testAddAndCmp() throws Exception { + testAddAndCmp(MUTABLE_ZERO, MUTABLE_ZERO, MUTABLE_ZERO); + testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, MUTABLE_ZERO); + testAddAndCmp(mutable("00000001", 0), mutable("00000001", 0), MUTABLE_ZERO); + testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000001", 0)); + testAddAndCmp(mutable("00000001", 0), mutable("00000002", 0), MUTABLE_ZERO); + testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000002", 0)); + testAddAndCmp(mutable("00000001", 2), mutable("FFFFFFFF", 0), mutable("FFFFFFFF", 0)); + testAddAndCmp(mutable("00000001", 0), mutable("00000001", 1), mutable("00000001", 0)); + + testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0F80000000", 1), mutable("F0F0F0F080000000", 1)); + testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0E80000000", 1), mutable("F0F0F0F080000000", 1)); + + testAddAndCmp(mutable("00000002", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1)); + testAddAndCmp(mutable("00000003", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1)); + testAddAndCmp(mutable("00000004", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1)); + testAddAndCmp(mutable("00000005", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1)); + + testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0)); + testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000001", 0)); + testAddAndCmp(mutable("00000002", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0)); + testAddAndCmp(mutable("00000003", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0)); + } + + private static void testMultBy10(FDBigInteger t, boolean isImmutable) throws Exception { + BigInteger bt = t.toBigInteger(); + FDBigInteger r = t.multBy10(); + if ((bt.signum() == 0 || !isImmutable) && r != t) { + throw new Exception("multBy10 of doesn't reuse its argument"); + } + if (isImmutable) { + check(bt, t, "multBy10 corrupts its argument"); + } + check(bt.multiply(BigInteger.TEN), r, "multBy10 returns wrong result"); + } + + private static void testMultBy10() throws Exception { + for (int p5 = 0; p5 <= MAX_P5; p5++) { + for (int p2 = 0; p2 <= MAX_P2; p2++) { + // This strange way of creating a value ensures that it is mutable. + FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2); + testMultBy10(value, false); + value.makeImmutable(); + testMultBy10(value, true); + } + } + } + + private static void testMultByPow52(FDBigInteger t, int p5, int p2) throws Exception { + BigInteger bt = t.toBigInteger(); + FDBigInteger r = t.multByPow52(p5, p2); + if (bt.signum() == 0 && r != t) { + throw new Exception("multByPow52 of doesn't reuse its argument"); + } + check(bt.multiply(biPow52(p5, p2)), r, "multByPow52 returns wrong result"); + } + + private static void testMultByPow52() throws Exception { + for (int p5 = 0; p5 <= MAX_P5; p5++) { + for (int p2 = 0; p2 <= MAX_P2; p2++) { + // This strange way of creating a value ensures that it is mutable. + FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2); + testMultByPow52(value, p5, p2); + } + } + } + + private static void testLeftInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception { + BigInteger biLeft = left.toBigInteger(); + BigInteger biRight = right.toBigInteger(); + FDBigInteger diff = left.leftInplaceSub(right); + if (!isImmutable && diff != left) { + throw new Exception("leftInplaceSub of doesn't reuse its argument"); + } + if (isImmutable) { + check(biLeft, left, "leftInplaceSub corrupts its left immutable argument"); + } + check(biRight, right, "leftInplaceSub corrupts its right argument"); + check(biLeft.subtract(biRight), diff, "leftInplaceSub returns wrong result"); + } + + private static void testLeftInplaceSub() throws Exception { + for (int p5 = 0; p5 <= MAX_P5; p5++) { + for (int p2 = 0; p2 <= MAX_P2; p2++) { +// for (int p5r = 0; p5r <= p5; p5r += 10) { +// for (int p2r = 0; p2r <= p2; p2r += 10) { + for (int p5r = 0; p5r <= p5; p5r++) { + for (int p2r = 0; p2r <= p2; p2r++) { + // This strange way of creating a value ensures that it is mutable. + FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2); + FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r); + testLeftInplaceSub(left, right, false); + left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2); + left.makeImmutable(); + testLeftInplaceSub(left, right, true); + } + } + } + } + } + + private static void testRightInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception { + BigInteger biLeft = left.toBigInteger(); + BigInteger biRight = right.toBigInteger(); + FDBigInteger diff = left.rightInplaceSub(right); + if (!isImmutable && diff != right) { + throw new Exception("rightInplaceSub of doesn't reuse its argument"); + } + check(biLeft, left, "leftInplaceSub corrupts its left argument"); + if (isImmutable) { + check(biRight, right, "leftInplaceSub corrupts its right immutable argument"); + } + try { + check(biLeft.subtract(biRight), diff, "rightInplaceSub returns wrong result"); + } catch (Exception e) { + System.out.println(biLeft+" - "+biRight+" = "+biLeft.subtract(biRight)); + throw e; + } + } + + private static void testRightInplaceSub() throws Exception { + for (int p5 = 0; p5 <= MAX_P5; p5++) { + for (int p2 = 0; p2 <= MAX_P2; p2++) { +// for (int p5r = 0; p5r <= p5; p5r += 10) { +// for (int p2r = 0; p2r <= p2; p2r += 10) { + for (int p5r = 0; p5r <= p5; p5r++) { + for (int p2r = 0; p2r <= p2; p2r++) { + // This strange way of creating a value ensures that it is mutable. + FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2); + FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r); + testRightInplaceSub(left, right, false); + right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r); + right.makeImmutable(); + testRightInplaceSub(left, right, true); + } + } + } + } + } + + public static void main(String[] args) throws Exception { + testValueOfPow52(); + testValueOfMulPow52(); + testLeftShift(); + testQuoRemIteration(); + testCmp(); + testCmpPow52(); + testAddAndCmp(); + // Uncomment the following for more comprehensize but slow testing. + // testLeftInplaceSub(); + // testMultBy10(); + // testMultByPow52(); + // testRightInplaceSub(); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/jdk/internal/math/FloatingDecimal/TestFloatingDecimal.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/jdk/internal/math/FloatingDecimal/TestFloatingDecimal.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,329 @@ +/* + * 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.Random; +import jdk.internal.math.FloatingDecimal; + +/* +OldFloatingDecimalForTest + +public class OldFloatingDecimalForTest { + public boolean digitsRoundedUp(); + public OldFloatingDecimalForTest(double); + public OldFloatingDecimalForTest(float); + public boolean decimalDigitsExact(); + public java.lang.String toString(); + public java.lang.String toJavaFormatString(); + public void appendTo(java.lang.Appendable); + public static OldFloatingDecimalForTest readJavaFormatString(java.lang.String) throws java.lang.NumberFormatException; + public strictfp double doubleValue(); + public strictfp float floatValue(); +} + +jdk.internal.math.FloatingDecimal + +public class jdk.internal.math.FloatingDecimal { + public jdk.internal.math.FloatingDecimal(); + public static java.lang.String toJavaFormatString(double); + public static java.lang.String toJavaFormatString(float); + public static void appendTo(double, java.lang.Appendable); + public static void appendTo(float, java.lang.Appendable); + public static double parseDouble(java.lang.String) throws java.lang.NumberFormatException; + public static float parseFloat(java.lang.String) throws java.lang.NumberFormatException; + public static jdk.internal.math.FloatingDecimal$AbstractD2ABuffer getD2ABuffer(double); +} +*/ + +/** + * @test + * @bug 7032154 + * @summary unit tests of FloatingDecimal + * @modules java.base/jdk.internal.math + * @library /java/lang/Math + * @build DoubleConsts FloatConsts + * @run main TestFloatingDecimal + * @author Brian Burkhalter + * @key randomness + */ +public class TestFloatingDecimal { + private static enum ResultType { + RESULT_EXCEPTION, + RESULT_PRINT + } + + private static final ResultType RESULT_TYPE = ResultType.RESULT_PRINT; + private static final int NUM_RANDOM_TESTS = 100000; + + private static final Random RANDOM = new Random(); + + private static void result(String message) { + switch (RESULT_TYPE) { + case RESULT_EXCEPTION: + throw new RuntimeException(message); + case RESULT_PRINT: + System.err.println(message); + break; + default: + assert false; + } + } + + private static int check(String test, Object expected, Object actual) { + int failures = 0; + if(!actual.equals(expected)) { + failures++; + result("Test "+test+" expected "+expected+" but obtained "+actual); + } + return failures; + } + + private static int testAppendToDouble() { + System.out.println(" testAppendToDouble"); + int failures = 0; + + for(int i = 0; i < NUM_RANDOM_TESTS; i++) { + double[] d = new double[] { + RANDOM.nextLong(), + RANDOM.nextGaussian(), + RANDOM.nextDouble()*Double.MAX_VALUE + }; + for(int j = 0; j < d.length; j++) { + OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]); + StringBuilder sb = new StringBuilder(); + ofd.appendTo(sb); + String oldString = sb.toString(); + sb = new StringBuilder(); + FloatingDecimal.appendTo(d[j], sb); + String newString = sb.toString(); + failures += check("testAppendToDouble", oldString, newString); + } + } + + return failures; + } + + private static int testAppendToFloat() { + System.out.println(" testAppendToFloat"); + int failures = 0; + + for(int i = 0; i < NUM_RANDOM_TESTS; i++) { + float[] f = new float[] { + RANDOM.nextLong(), + (float)RANDOM.nextGaussian(), + RANDOM.nextFloat()*Float.MAX_VALUE + }; + for(int j = 0; j < f.length; j++) { + OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]); + StringBuilder sb = new StringBuilder(); + ofd.appendTo(sb); + String oldString = sb.toString(); + sb = new StringBuilder(); + FloatingDecimal.appendTo(f[j], sb); + String newString = sb.toString(); + failures += check("testAppendToFloat", oldString, newString); + } + } + + return failures; + } + + private static int testAppendTo() { + System.out.println("testAppendTo"); + int failures = 0; + + failures += testAppendToDouble(); + failures += testAppendToFloat(); + + return failures; + } + + private static int testParseDouble() { + System.out.println(" testParseDouble"); + int failures = 0; + + for(int i = 0; i < NUM_RANDOM_TESTS; i++) { + double[] d = new double[] { + RANDOM.nextLong(), + RANDOM.nextGaussian(), + RANDOM.nextDouble()*Double.MAX_VALUE + }; + for(int j = 0; j < d.length; j++) { + OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]); + String javaFormatString = ofd.toJavaFormatString(); + ofd = OldFloatingDecimalForTest.readJavaFormatString(javaFormatString); + double oldDouble = ofd.doubleValue(); + double newDouble = FloatingDecimal.parseDouble(javaFormatString); + failures += check("testParseDouble", oldDouble, newDouble); + } + } + + return failures; + } + + private static int testParseFloat() { + System.out.println(" testParseFloat"); + int failures = 0; + + for(int i = 0; i < NUM_RANDOM_TESTS; i++) { + float[] f = new float[] { + RANDOM.nextInt(), + (float)RANDOM.nextGaussian(), + RANDOM.nextFloat()*Float.MAX_VALUE + }; + for(int j = 0; j < f.length; j++) { + OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]); + String javaFormatString = ofd.toJavaFormatString(); + ofd = OldFloatingDecimalForTest.readJavaFormatString(javaFormatString); + float oldFloat = ofd.floatValue(); + float newFloat = FloatingDecimal.parseFloat(javaFormatString); + failures += check("testParseFloat", oldFloat, newFloat); + } + } + + return failures; + } + + private static int testParse() { + System.out.println("testParse"); + int failures = 0; + + failures += testParseDouble(); + failures += testParseFloat(); + + return failures; + } + + private static int testToJavaFormatStringDoubleFixed() { + System.out.println(" testToJavaFormatStringDoubleFixed"); + int failures = 0; + + double[] d = new double [] { + -5.9522650387500933e18, // dtoa() fast path + 0.872989018674569, // dtoa() fast iterative - long + 1.1317400099603851e308 // dtoa() slow iterative + }; + + for(int i = 0; i < d.length; i++) { + OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[i]); + failures += check("testToJavaFormatStringDoubleFixed", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(d[i])); + } + + return failures; + } + + private static int testToJavaFormatStringDoubleRandom() { + System.out.println(" testToJavaFormatStringDoubleRandom"); + int failures = 0; + + for(int i = 0; i < NUM_RANDOM_TESTS; i++) { + double[] d = new double[] { + RANDOM.nextLong(), + RANDOM.nextGaussian(), + RANDOM.nextDouble()*Double.MAX_VALUE + }; + for(int j = 0; j < d.length; j++) { + OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]); + failures += check("testToJavaFormatStringDoubleRandom", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(d[j])); + } + } + + return failures; + } + + private static int testToJavaFormatStringDouble() { + System.out.println(" testToJavaFormatStringDouble"); + int failures = 0; + failures += testToJavaFormatStringDoubleFixed(); + failures += testToJavaFormatStringDoubleRandom(); + return failures; + } + + private static int testToJavaFormatStringFloatFixed() { + System.out.println(" testToJavaFormatStringFloatFixed"); + int failures = 0; + + float[] f = new float[] { + -9.8784166e8f, // dtoa() fast path + 0.70443946f, // dtoa() fast iterative - int + 1.8254228e37f // dtoa() slow iterative + }; + + for(int i = 0; i < f.length; i++) { + OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[i]); + failures += check("testToJavaFormatStringFloatFixed", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(f[i])); + } + + return failures; + } + + private static int testToJavaFormatStringFloatRandom() { + System.out.println(" testToJavaFormatStringFloatRandom"); + int failures = 0; + + for(int i = 0; i < NUM_RANDOM_TESTS; i++) { + float[] f = new float[] { + RANDOM.nextInt(), + (float)RANDOM.nextGaussian(), + RANDOM.nextFloat()*Float.MAX_VALUE + }; + for(int j = 0; j < f.length; j++) { + OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]); + failures += check("testToJavaFormatStringFloatRandom", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(f[j])); + } + } + + return failures; + } + + private static int testToJavaFormatStringFloat() { + System.out.println(" testToJavaFormatStringFloat"); + int failures = 0; + + failures += testToJavaFormatStringFloatFixed(); + failures += testToJavaFormatStringFloatRandom(); + + return failures; + } + + private static int testToJavaFormatString() { + System.out.println("testToJavaFormatString"); + int failures = 0; + + failures += testToJavaFormatStringDouble(); + failures += testToJavaFormatStringFloat(); + + return failures; + } + + public static void main(String[] args) { + int failures = 0; + + failures += testAppendTo(); + failures += testParse(); + failures += testToJavaFormatString(); + + if (failures != 0) { + throw new RuntimeException("" + failures + " failures while testing FloatingDecimal"); + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/jdk/nio/zipfs/MultiReleaseJarTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/jdk/nio/zipfs/MultiReleaseJarTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8144355 + * @summary Test aliasing additions to ZipFileSystem for multi-release jar files + * @library /lib/testlibrary/java/util/jar + * @build Compiler JarBuilder CreateMultiReleaseTestJars + * @run testng MultiReleaseJarTest + */ + +import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.net.URI; +import java.nio.file.*; +import java.util.HashMap; +import java.util.Map; + +import static sun.misc.Version.jdkMajorVersion; + +import org.testng.Assert; +import org.testng.annotations.*; + +public class MultiReleaseJarTest { + final private String userdir = System.getProperty("user.dir","."); + final private Map stringEnv = new HashMap<>(); + final private Map integerEnv = new HashMap<>(); + final private String className = "version.Version"; + final private MethodType mt = MethodType.methodType(int.class); + + private String entryName; + private URI uvuri; + private URI mruri; + private URI smruri; + + @BeforeClass + public void initialize() throws Exception { + CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars(); + creator.compileEntries(); + creator.buildUnversionedJar(); + creator.buildMultiReleaseJar(); + creator.buildShortMultiReleaseJar(); + String ssp = Paths.get(userdir, "unversioned.jar").toUri().toString(); + uvuri = new URI("jar", ssp , null); + ssp = Paths.get(userdir, "multi-release.jar").toUri().toString(); + mruri = new URI("jar", ssp, null); + ssp = Paths.get(userdir, "short-multi-release.jar").toUri().toString(); + smruri = new URI("jar", ssp, null); + entryName = className.replace('.', '/') + ".class"; + } + + public void close() throws IOException { + Files.delete(Paths.get(userdir, "unversioned.jar")); + Files.delete(Paths.get(userdir, "multi-release.jar")); + Files.delete(Paths.get(userdir, "short-multi-release.jar")); + } + + @DataProvider(name="strings") + public Object[][] createStrings() { + return new Object[][]{ + {"runtime", jdkMajorVersion()}, + {"-20", 8}, + {"0", 8}, + {"8", 8}, + {"9", 9}, + {"10", 10}, + {"11", 10}, + {"50", 10} + }; + } + + @DataProvider(name="integers") + public Object[][] createIntegers() { + return new Object[][] { + {new Integer(-5), 8}, + {new Integer(0), 8}, + {new Integer(8), 8}, + {new Integer(9), 9}, + {new Integer(10), 10}, + {new Integer(11), 10}, + {new Integer(100), 10} + }; + } + + // Not the best test but all I can do since ZipFileSystem and JarFileSystem + // are not public, so I can't use (fs instanceof ...) + @Test + public void testNewFileSystem() throws Exception { + Map env = new HashMap<>(); + // no configuration, treat multi-release jar as unversioned + try (FileSystem fs = FileSystems.newFileSystem(mruri, env)) { + Assert.assertTrue(readAndCompare(fs, 8)); + } + env.put("multi-release", "runtime"); + // a configuration and jar file is multi-release + try (FileSystem fs = FileSystems.newFileSystem(mruri, env)) { + Assert.assertTrue(readAndCompare(fs, jdkMajorVersion())); + } + // a configuration but jar file is unversioned + try (FileSystem fs = FileSystems.newFileSystem(uvuri, env)) { + Assert.assertTrue(readAndCompare(fs, 8)); + } + } + + private boolean readAndCompare(FileSystem fs, int expected) throws IOException { + Path path = fs.getPath("version/Version.java"); + String src = new String(Files.readAllBytes(path)); + return src.contains("return " + expected); + } + + @Test(dataProvider="strings") + public void testStrings(String value, int expected) throws Throwable { + stringEnv.put("multi-release", value); + runTest(stringEnv, expected); + } + + @Test(dataProvider="integers") + public void testIntegers(Integer value, int expected) throws Throwable { + integerEnv.put("multi-release", value); + runTest(integerEnv, expected); + } + + @Test + public void testShortJar() throws Throwable { + integerEnv.put("multi-release", Integer.valueOf(10)); + runTest(smruri, integerEnv, 10); + integerEnv.put("multi-release", Integer.valueOf(9)); + runTest(smruri, integerEnv, 8); + } + + private void runTest(Map env, int expected) throws Throwable { + runTest(mruri, env, expected); + } + + private void runTest(URI uri, Map env, int expected) throws Throwable { + try (FileSystem fs = FileSystems.newFileSystem(uri, env)) { + Path version = fs.getPath(entryName); + byte [] bytes = Files.readAllBytes(version); + Class vcls = (new ByteArrayClassLoader(fs)).defineClass(className, bytes); + MethodHandle mh = MethodHandles.lookup().findVirtual(vcls, "getVersion", mt); + Assert.assertEquals((int)mh.invoke(vcls.newInstance()), expected); + } + } + + private static class ByteArrayClassLoader extends ClassLoader { + final private FileSystem fs; + + ByteArrayClassLoader(FileSystem fs) { + super(null); + this.fs = fs; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + try { + return super.loadClass(name); + } catch (ClassNotFoundException x) {} + Path cls = fs.getPath(name.replace('.', '/') + ".class"); + try { + byte[] bytes = Files.readAllBytes(cls); + return defineClass(name, bytes); + } catch (IOException x) { + throw new ClassNotFoundException(x.getMessage()); + } + } + + public Class defineClass(String name, byte[] bytes) throws ClassNotFoundException { + if (bytes == null) throw new ClassNotFoundException("No bytes for " + name); + return defineClass(name, bytes, 0, bytes.length); + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/lib/testlibrary/java/util/jar/Compiler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/lib/testlibrary/java/util/jar/Compiler.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 javax.tools.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +class Compiler { + final private Map input; + private List options; + + Compiler(Map input) { + this.input = input; + } + + Compiler setRelease(int release) { + // Setting the -release option does not work for some reason + // so do it the old fashioned way + // options = Arrays.asList("-release", String.valueOf(release)); + String target = String.valueOf(release); + options = Arrays.asList("-source", target, "-target", target); + return this; + } + + Map compile() { + List cunits = createCompilationUnits(); + Map cfos = createClassFileObjects(); + JavaCompiler jc = ToolProvider.getSystemJavaCompiler(); + JavaFileManager jfm = new CustomFileManager(jc.getStandardFileManager(null, null, null), cfos); + jc.getTask(null, jfm, null, options, null, cunits).call(); + return createOutput(cfos); + } + + private List createCompilationUnits() { + return input.entrySet().stream() + .map(e -> new SourceFileObject(e.getKey(), e.getValue())).collect(Collectors.toList()); + } + + private Map createClassFileObjects() { + return input.keySet().stream() + .collect(Collectors.toMap(k -> k, k -> new ClassFileObject(k))); + } + + private Map createOutput(Map cfos) { + return cfos.keySet().stream().collect(Collectors.toMap(k -> k, k -> cfos.get(k).getBytes())); + } + + private static class SourceFileObject extends SimpleJavaFileObject { + private final String source; + + SourceFileObject(String name, String source) { + super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); + this.source = source; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return source; + } + } + + private static class ClassFileObject extends SimpleJavaFileObject { + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + ClassFileObject(String className) { + super(URI.create(className), Kind.CLASS); + } + + @Override + public OutputStream openOutputStream() throws IOException { + return baos; + } + + public byte[] getBytes() { + return baos.toByteArray(); + } + } + + private static class CustomFileManager extends ForwardingJavaFileManager { + private final Map cfos; + + CustomFileManager(JavaFileManager jfm, Map cfos) { + super(jfm); + this.cfos = cfos; + } + + @Override + public JavaFileObject getJavaFileForOutput(JavaFileManager.Location loc, String name, + JavaFileObject.Kind kind, FileObject sibling) throws IOException { + ClassFileObject cfo = cfos.get(name); + return cfo; + } + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +public class CreateMultiReleaseTestJars { + final private String main = + "package version;\n\n" + + "public class Main {\n" + + " public static void main(String[] args) {\n" + + " Version v = new Version();\n" + + " System.out.println(\"I am running on version \" + v.getVersion());\n" + + " }\n" + + "}\n"; + final private String java8 = + "package version;\n\n" + + "public class Version {\n" + + " public int getVersion() {\n" + + " return 8;\n" + + " }\n" + + "}\n"; + final private String java9 = + "package version;\n\n" + + "public class Version {\n" + + " public int getVersion() {\n" + + " int version = (new PackagePrivate()).getVersion();\n" + + " if (version == 9) return 9;\n" // strange I know, but easy to test + + " return version;\n" + + " }\n" + + "}\n"; + final private String ppjava9 = + "package version;\n\n" + + "class PackagePrivate {\n" + + " int getVersion() {\n" + + " return 9;\n" + + " }\n" + + "}\n"; + final private String java10 = java8.replace("8", "10"); + final String readme8 = "This is the root readme file"; + final String readme9 = "This is the version nine readme file"; + final String readme10 = "This is the version ten readme file"; + private Map rootClasses; + private Map version9Classes; + private Map version10Classes; + + public void buildUnversionedJar() throws IOException { + JarBuilder jb = new JarBuilder("unversioned.jar"); + jb.addEntry("README", readme8.getBytes()); + jb.addEntry("version/Main.java", main.getBytes()); + jb.addEntry("version/Main.class", rootClasses.get("version.Main")); + jb.addEntry("version/Version.java", java8.getBytes()); + jb.addEntry("version/Version.class", rootClasses.get("version.Version")); + jb.build(); + } + + public void buildMultiReleaseJar() throws IOException { + JarBuilder jb = new JarBuilder("multi-release.jar"); + jb.addAttribute("Multi-Release", "true"); + jb.addEntry("README", readme8.getBytes()); + jb.addEntry("version/Main.java", main.getBytes()); + jb.addEntry("version/Main.class", rootClasses.get("version.Main")); + jb.addEntry("version/Version.java", java8.getBytes()); + jb.addEntry("version/Version.class", rootClasses.get("version.Version")); + jb.addEntry("META-INF/versions/9/README", readme9.getBytes()); + jb.addEntry("META-INF/versions/9/version/Version.java", java9.getBytes()); + jb.addEntry("META-INF/versions/9/version/PackagePrivate.java", ppjava9.getBytes()); + jb.addEntry("META-INF/versions/9/version/Version.class", version9Classes.get("version.Version")); + jb.addEntry("META-INF/versions/9/version/PackagePrivate.class", version9Classes.get("version.PackagePrivate")); + jb.addEntry("META-INF/versions/10/README", readme10.getBytes()); + jb.addEntry("META-INF/versions/10/version/Version.java", java10.getBytes()); + jb.addEntry("META-INF/versions/10/version/Version.class", version10Classes.get("version.Version")); + jb.build(); + } + + public void buildShortMultiReleaseJar() throws IOException { + JarBuilder jb = new JarBuilder("short-multi-release.jar"); + jb.addAttribute("Multi-Release", "true"); + jb.addEntry("README", readme8.getBytes()); + jb.addEntry("version/Main.java", main.getBytes()); + jb.addEntry("version/Main.class", rootClasses.get("version.Main")); + jb.addEntry("version/Version.java", java8.getBytes()); + jb.addEntry("version/Version.class", rootClasses.get("version.Version")); + jb.addEntry("META-INF/versions/9/README", readme9.getBytes()); + jb.addEntry("META-INF/versions/9/version/Version.java", java9.getBytes()); + jb.addEntry("META-INF/versions/9/version/PackagePrivate.java", ppjava9.getBytes()); + // no entry for META-INF/versions/9/version/Version.class + jb.addEntry("META-INF/versions/9/version/PackagePrivate.class", version9Classes.get("version.PackagePrivate")); + jb.addEntry("META-INF/versions/10/README", readme10.getBytes()); + jb.addEntry("META-INF/versions/10/version/Version.java", java10.getBytes()); + jb.addEntry("META-INF/versions/10/version/Version.class", version10Classes.get("version.Version")); + jb.build(); + } + + public void buildSignedMultiReleaseJar() throws Exception { + String testsrc = System.getProperty("test.src","."); + String testdir = findTestDir(testsrc); + String keystore = testdir + "/sun/security/tools/jarsigner/JarSigning.keystore"; + String[] jsArgs = { + "-keystore", keystore, + "-storepass", "bbbbbb", + "-signedJar", "signed-multi-release.jar", + "multi-release.jar", "b" + }; + sun.security.tools.jarsigner.Main.main(jsArgs); + + } + + String findTestDir(String dir) throws IOException { + Path path = Paths.get(dir).toAbsolutePath(); + while (path != null && !path.endsWith("test")) { + path = path.getParent(); + } + if (path == null) { + throw new IllegalArgumentException(dir + " is not in a test directory"); + } + if (!Files.isDirectory(path)) { + throw new IOException(path.toString() + " is not a directory"); + } + return path.toString(); + } + + void compileEntries() { + Map input = new HashMap<>(); + input.put("version.Main", main); + input.put("version.Version", java8); + rootClasses = (new Compiler(input)).setRelease(8).compile(); + input.clear(); + input.put("version.Version", java9); + input.put("version.PackagePrivate", ppjava9); + version9Classes = (new Compiler(input)).setRelease(9).compile(); + input.clear(); + input.put("version.Version", java10); + version10Classes = (new Compiler(input)).setRelease(9).compile(); // fixme in JDK 10 + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/lib/testlibrary/java/util/jar/JarBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/lib/testlibrary/java/util/jar/JarBuilder.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +public class JarBuilder { + final private String name; + final private Attributes attributes = new Attributes(); + final private List entries = new ArrayList<>(); + + public JarBuilder(String name) { + this.name = name; + attributes.putValue("Manifest-Version", "1.0"); + attributes.putValue("Created-By", "1.9.0-internal (Oracle Corporation)"); + } + + public JarBuilder addAttribute(String name, String value) { + attributes.putValue(name, value); + return this; + } + + public JarBuilder addEntry(String name, byte[] bytes) { + entries.add(new Entry(name, bytes)); + return this; + } + + public void build() throws IOException { + try (OutputStream os = Files.newOutputStream(Paths.get(name)); + JarOutputStream jos = new JarOutputStream(os)) { + JarEntry me = new JarEntry("META-INF/MANIFEST.MF"); + jos.putNextEntry(me); + Manifest manifest = new Manifest(); + manifest.getMainAttributes().putAll(attributes); + manifest.write(jos); + jos.closeEntry(); + entries.forEach(e -> { + JarEntry je = new JarEntry(e.name); + try { + jos.putNextEntry(je); + jos.write(e.bytes); + jos.closeEntry(); + } catch (IOException iox) { + throw new RuntimeException(iox); + } + }); + } catch (RuntimeException x) { + Throwable t = x.getCause(); + if (t instanceof IOException) { + IOException iox = (IOException)t; + throw iox; + } + throw x; + } + } + + private static class Entry { + String name; + byte[] bytes; + + Entry(String name, byte[] bytes) { + this.name = name; + this.bytes = bytes; + } + } + + public static void main(String[] args) throws IOException { + JarBuilder jb = new JarBuilder("version.jar"); + jb.addAttribute("Multi-Release", "true"); + String s = "something to say"; + byte[] bytes = s.getBytes(); + jb.addEntry("version/Version.class", bytes); + jb.addEntry("README", bytes); + jb.addEntry("version/Version.java", bytes); + jb.build(); + } +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/sun/java2d/marlin/ArrayCacheSizeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/java2d/marlin/ArrayCacheSizeTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 sun.java2d.marlin.ArrayCache; + +/** + * @test + * @bug 8144445 + * @summary Check the ArrayCache getNewLargeSize() method + * @run main ArrayCacheSizeTest + */ +public class ArrayCacheSizeTest { + + public static void main(String[] args) { + testNewSize(); + testNewLargeSize(); + } + + private static void testNewSize() { + testNewSize(0, 1); + testNewSize(0, 100000); + + testNewSize(4096, 4097); + testNewSize(4096 * 16, 4096 * 16 + 1); + + testNewSize(4096 * 4096 * 4, 4096 * 4096 * 4 + 1); + + testNewSize(4096 * 4096 * 4, Integer.MAX_VALUE); + + testNewSize(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE); + + testNewSizeExpectAIOB(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE + 1); + testNewSizeExpectAIOB(1, -1); + testNewSizeExpectAIOB(Integer.MAX_VALUE, -1); + } + + private static void testNewSizeExpectAIOB(final int curSize, + final int needSize) { + try { + testNewSize(curSize, needSize); + throw new RuntimeException("ArrayIndexOutOfBoundsException not thrown"); + } catch (ArrayIndexOutOfBoundsException aiobe) { + System.out.println("ArrayIndexOutOfBoundsException expected."); + } catch (RuntimeException re) { + throw re; + } catch (Throwable th) { + throw new RuntimeException("Unexpected exception", th); + } + } + + private static void testNewSize(final int curSize, + final int needSize) { + + int size = ArrayCache.getNewSize(curSize, needSize); + + System.out.println("getNewSize(" + curSize + ", " + needSize + + ") = " + size); + + if (size < 0 || size < needSize) { + throw new IllegalStateException("Invalid getNewSize(" + + curSize + ", " + needSize + ") = " + size + " !"); + } + } + + private static void testNewLargeSize() { + testNewLargeSize(0, 1); + testNewLargeSize(0, 100000); + + testNewLargeSize(4096, 4097); + testNewLargeSize(4096 * 16, 4096 * 16 + 1); + + testNewLargeSize(4096 * 4096 * 4, 4096 * 4096 * 4 + 1); + + testNewLargeSize(4096 * 4096 * 4, Integer.MAX_VALUE); + + testNewLargeSize(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE); + + testNewLargeSizeExpectAIOB(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE + 1L); + testNewLargeSizeExpectAIOB(1, -1L); + testNewLargeSizeExpectAIOB(Integer.MAX_VALUE, -1L); + } + + private static void testNewLargeSizeExpectAIOB(final long curSize, + final long needSize) { + try { + testNewLargeSize(curSize, needSize); + throw new RuntimeException("ArrayIndexOutOfBoundsException not thrown"); + } catch (ArrayIndexOutOfBoundsException aiobe) { + System.out.println("ArrayIndexOutOfBoundsException expected."); + } catch (RuntimeException re) { + throw re; + } catch (Throwable th) { + throw new RuntimeException("Unexpected exception", th); + } + } + + private static void testNewLargeSize(final long curSize, + final long needSize) { + + long size = ArrayCache.getNewLargeSize(curSize, needSize); + + System.out.println("getNewLargeSize(" + curSize + ", " + needSize + + ") = " + size); + + if (size < 0 || size < needSize || size > Integer.MAX_VALUE) { + throw new IllegalStateException("Invalid getNewLargeSize(" + + curSize + ", " + needSize + ") = " + size + " !"); + } + } + +} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/sun/java2d/marlin/CrashTest.java --- a/jdk/test/sun/java2d/marlin/CrashTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/sun/java2d/marlin/CrashTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -31,31 +31,44 @@ import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; -import sun.java2d.pipe.RenderingEngine; /** - * Simple crash rendering test using huge GeneralPaths with marlin renderer - * - * run it with large heap (2g): - * java -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine marlin.CrashTest - * - * @author bourgesl - */ + * @test + * @summary Simple crash rendering test using huge GeneralPaths with the Marlin renderer + * @run main/othervm -mx512m CrashTest + * @ignore tests that take a long time and consumes 5Gb memory + * @run main/othervm -ms4g -mx4g CrashTest -slow +*/ public class CrashTest { static final boolean SAVE_IMAGE = false; static boolean USE_ROUND_CAPS_AND_JOINS = true; public static void main(String[] args) { + boolean runSlowTests = (args.length != 0 && "-slow".equals(args[0])); + + // First display which renderer is tested: + System.setProperty("sun.java2d.renderer.verbose", "true"); + // try insane image sizes: // subpixel coords may overflow: -// testHugeImage((Integer.MAX_VALUE >> 3) + 1, 6); + // check MAX_VALUE / (8 * 2); overflow may happen due to orientation flag + // But as it is impossible to allocate an image larger than 2Gb (byte) then + // it is also impossible to have rowAAChunk larger than 2Gb ! + + // Disabled test as it consumes 4GB heap + offheap (2Gb) ie > 6Gb ! + if (runSlowTests) { + testHugeImage((Integer.MAX_VALUE >> 4) - 100, 16); + } + // larger than 23 bits: (RLE) testHugeImage(8388608 + 1, 10); - test(0.1f, false, 0); - test(0.1f, true, 7f); + if (runSlowTests) { + test(0.1f, false, 0); + test(0.1f, true, 7f); + } // Exceed 2Gb OffHeap buffer for edges: try { @@ -67,17 +80,15 @@ if (th instanceof ArrayIndexOutOfBoundsException) { System.out.println("ArrayIndexOutOfBoundsException expected."); } else { - System.out.println("Exception occured:"); - th.printStackTrace(); + throw new RuntimeException("Unexpected exception", th); } } - } private static void test(final float lineStroke, final boolean useDashes, final float dashMinLen) - throws ArrayIndexOutOfBoundsException + throws ArrayIndexOutOfBoundsException { System.out.println("---\n" + "test: " + "lineStroke=" + lineStroke @@ -85,9 +96,6 @@ +", dashMinLen=" + dashMinLen ); - final String renderer = RenderingEngine.getInstance().getClass().getSimpleName(); - System.out.println("Testing renderer = " + renderer); - final BasicStroke stroke = createStroke(lineStroke, useDashes, dashMinLen); // TODO: test Dasher.firstSegmentsBuffer resizing ? @@ -135,7 +143,7 @@ if (SAVE_IMAGE) { try { - final File file = new File("CrashTest-" + renderer + "-dash-" + useDashes + ".bmp"); + final File file = new File("CrashTest-dash-" + useDashes + ".bmp"); System.out.println("Writing file: " + file.getAbsolutePath()); ImageIO.write(image, "BMP", file); @@ -150,15 +158,10 @@ } private static void testHugeImage(final int width, final int height) - throws ArrayIndexOutOfBoundsException + throws ArrayIndexOutOfBoundsException { System.out.println("---\n" + "testHugeImage: " - + "width=" + width - + ", height=" + height - ); - - final String renderer = RenderingEngine.getInstance().getClass().getSimpleName(); - System.out.println("Testing renderer = " + renderer); + + "width=" + width + ", height=" + height); final BasicStroke stroke = createStroke(2.5f, false, 0); @@ -195,8 +198,8 @@ if (SAVE_IMAGE) { try { - final File file = new File("CrashTest-" + renderer + - "-huge-" + width + "x" +height + ".bmp"); + final File file = new File("CrashTest-huge-" + + width + "x" +height + ".bmp"); System.out.println("Writing file: " + file.getAbsolutePath()); ImageIO.write(image, "BMP", file); diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java --- a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java Wed Jul 05 21:10:34 2017 +0200 @@ -58,7 +58,7 @@ * @run main/othervm/timeout=600 -XX:+UsePerfData JMXStartStopTest * @summary Makes sure that enabling/disabling the management agent through JCMD * achieves the desired results - * @key randomness + * @key randomness intermittent */ public class JMXStartStopTest { private static final String TEST_APP_NAME = "TestApp"; diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/sun/misc/FloatingDecimal/OldFDBigIntForTest.java --- a/jdk/test/sun/misc/FloatingDecimal/OldFDBigIntForTest.java Wed Dec 23 15:41:50 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,491 +0,0 @@ -/* - * Copyright (c) 1996, 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 sun.misc; - -/* - * A really, really simple bigint package - * tailored to the needs of floating base conversion. - */ -class OldFDBigIntForTest { - int nWords; // number of words used - int data[]; // value: data[0] is least significant - - - public OldFDBigIntForTest( int v ){ - nWords = 1; - data = new int[1]; - data[0] = v; - } - - public OldFDBigIntForTest( long v ){ - data = new int[2]; - data[0] = (int)v; - data[1] = (int)(v>>>32); - nWords = (data[1]==0) ? 1 : 2; - } - - public OldFDBigIntForTest( OldFDBigIntForTest other ){ - data = new int[nWords = other.nWords]; - System.arraycopy( other.data, 0, data, 0, nWords ); - } - - private OldFDBigIntForTest( int [] d, int n ){ - data = d; - nWords = n; - } - - public OldFDBigIntForTest( long seed, char digit[], int nd0, int nd ){ - int n= (nd+8)/9; // estimate size needed. - if ( n < 2 ) n = 2; - data = new int[n]; // allocate enough space - data[0] = (int)seed; // starting value - data[1] = (int)(seed>>>32); - nWords = (data[1]==0) ? 1 : 2; - int i = nd0; - int limit = nd-5; // slurp digits 5 at a time. - int v; - while ( i < limit ){ - int ilim = i+5; - v = (int)digit[i++]-(int)'0'; - while( i >5; - int bitcount = c & 0x1f; - int anticount = 32-bitcount; - int t[] = data; - int s[] = data; - if ( nWords+wordcount+1 > t.length ){ - // reallocate. - t = new int[ nWords+wordcount+1 ]; - } - int target = nWords+wordcount; - int src = nWords-1; - if ( bitcount == 0 ){ - // special hack, since an anticount of 32 won't go! - System.arraycopy( s, 0, t, wordcount, nWords ); - target = wordcount-1; - } else { - t[target--] = s[src]>>>anticount; - while ( src >= 1 ){ - t[target--] = (s[src]<>>anticount); - } - t[target--] = s[src]<= 0 ){ - t[target--] = 0; - } - data = t; - nWords += wordcount + 1; - // may have constructed high-order word of 0. - // if so, trim it - while ( nWords > 1 && data[nWords-1] == 0 ) - nWords--; - } - - /* - * normalize this number by shifting until - * the MSB of the number is at 0x08000000. - * This is in preparation for quoRemIteration, below. - * The idea is that, to make division easier, we want the - * divisor to be "normalized" -- usually this means shifting - * the MSB into the high words sign bit. But because we know that - * the quotient will be 0 < q < 10, we would like to arrange that - * the dividend not span up into another word of precision. - * (This needs to be explained more clearly!) - */ - public int - normalizeMe() throws IllegalArgumentException { - int src; - int wordcount = 0; - int bitcount = 0; - int v = 0; - for ( src= nWords-1 ; src >= 0 && (v=data[src]) == 0 ; src--){ - wordcount += 1; - } - if ( src < 0 ){ - // oops. Value is zero. Cannot normalize it! - throw new IllegalArgumentException("zero value"); - } - /* - * In most cases, we assume that wordcount is zero. This only - * makes sense, as we try not to maintain any high-order - * words full of zeros. In fact, if there are zeros, we will - * simply SHORTEN our number at this point. Watch closely... - */ - nWords -= wordcount; - /* - * Compute how far left we have to shift v s.t. its highest- - * order bit is in the right place. Then call lshiftMe to - * do the work. - */ - if ( (v & 0xf0000000) != 0 ){ - // will have to shift up into the next word. - // too bad. - for( bitcount = 32 ; (v & 0xf0000000) != 0 ; bitcount-- ) - v >>>= 1; - } else { - while ( v <= 0x000fffff ){ - // hack: byte-at-a-time shifting - v <<= 8; - bitcount += 8; - } - while ( v <= 0x07ffffff ){ - v <<= 1; - bitcount += 1; - } - } - if ( bitcount != 0 ) - lshiftMe( bitcount ); - return bitcount; - } - - /* - * Multiply a OldFDBigIntForTest by an int. - * Result is a new OldFDBigIntForTest. - */ - public OldFDBigIntForTest - mult( int iv ) { - long v = iv; - int r[]; - long p; - - // guess adequate size of r. - r = new int[ ( v * ((long)data[nWords-1]&0xffffffffL) > 0xfffffffL ) ? nWords+1 : nWords ]; - p = 0L; - for( int i=0; i < nWords; i++ ) { - p += v * ((long)data[i]&0xffffffffL); - r[i] = (int)p; - p >>>= 32; - } - if ( p == 0L){ - return new OldFDBigIntForTest( r, nWords ); - } else { - r[nWords] = (int)p; - return new OldFDBigIntForTest( r, nWords+1 ); - } - } - - /* - * Multiply a OldFDBigIntForTest by an int and add another int. - * Result is computed in place. - * Hope it fits! - */ - public void - multaddMe( int iv, int addend ) { - long v = iv; - long p; - - // unroll 0th iteration, doing addition. - p = v * ((long)data[0]&0xffffffffL) + ((long)addend&0xffffffffL); - data[0] = (int)p; - p >>>= 32; - for( int i=1; i < nWords; i++ ) { - p += v * ((long)data[i]&0xffffffffL); - data[i] = (int)p; - p >>>= 32; - } - if ( p != 0L){ - data[nWords] = (int)p; // will fail noisily if illegal! - nWords++; - } - } - - /* - * Multiply a OldFDBigIntForTest by another OldFDBigIntForTest. - * Result is a new OldFDBigIntForTest. - */ - public OldFDBigIntForTest - mult( OldFDBigIntForTest other ){ - // crudely guess adequate size for r - int r[] = new int[ nWords + other.nWords ]; - int i; - // I think I am promised zeros... - - for( i = 0; i < this.nWords; i++ ){ - long v = (long)this.data[i] & 0xffffffffL; // UNSIGNED CONVERSION - long p = 0L; - int j; - for( j = 0; j < other.nWords; j++ ){ - p += ((long)r[i+j]&0xffffffffL) + v*((long)other.data[j]&0xffffffffL); // UNSIGNED CONVERSIONS ALL 'ROUND. - r[i+j] = (int)p; - p >>>= 32; - } - r[i+j] = (int)p; - } - // compute how much of r we actually needed for all that. - for ( i = r.length-1; i> 0; i--) - if ( r[i] != 0 ) - break; - return new OldFDBigIntForTest( r, i+1 ); - } - - /* - * Add one OldFDBigIntForTest to another. Return a OldFDBigIntForTest - */ - public OldFDBigIntForTest - add( OldFDBigIntForTest other ){ - int i; - int a[], b[]; - int n, m; - long c = 0L; - // arrange such that a.nWords >= b.nWords; - // n = a.nWords, m = b.nWords - if ( this.nWords >= other.nWords ){ - a = this.data; - n = this.nWords; - b = other.data; - m = other.nWords; - } else { - a = other.data; - n = other.nWords; - b = this.data; - m = this.nWords; - } - int r[] = new int[ n ]; - for ( i = 0; i < n; i++ ){ - c += (long)a[i] & 0xffffffffL; - if ( i < m ){ - c += (long)b[i] & 0xffffffffL; - } - r[i] = (int) c; - c >>= 32; // signed shift. - } - if ( c != 0L ){ - // oops -- carry out -- need longer result. - int s[] = new int[ r.length+1 ]; - System.arraycopy( r, 0, s, 0, r.length ); - s[i++] = (int)c; - return new OldFDBigIntForTest( s, i ); - } - return new OldFDBigIntForTest( r, i ); - } - - /* - * Subtract one OldFDBigIntForTest from another. Return a OldFDBigIntForTest - * Assert that the result is positive. - */ - public OldFDBigIntForTest - sub( OldFDBigIntForTest other ){ - int r[] = new int[ this.nWords ]; - int i; - int n = this.nWords; - int m = other.nWords; - int nzeros = 0; - long c = 0L; - for ( i = 0; i < n; i++ ){ - c += (long)this.data[i] & 0xffffffffL; - if ( i < m ){ - c -= (long)other.data[i] & 0xffffffffL; - } - if ( ( r[i] = (int) c ) == 0 ) - nzeros++; - else - nzeros = 0; - c >>= 32; // signed shift - } - assert c == 0L : c; // borrow out of subtract - assert dataInRangeIsZero(i, m, other); // negative result of subtract - return new OldFDBigIntForTest( r, n-nzeros ); - } - - private static boolean dataInRangeIsZero(int i, int m, OldFDBigIntForTest other) { - while ( i < m ) - if (other.data[i++] != 0) - return false; - return true; - } - - /* - * Compare OldFDBigIntForTest with another OldFDBigIntForTest. Return an integer - * >0: this > other - * 0: this == other - * <0: this < other - */ - public int - cmp( OldFDBigIntForTest other ){ - int i; - if ( this.nWords > other.nWords ){ - // if any of my high-order words is non-zero, - // then the answer is evident - int j = other.nWords-1; - for ( i = this.nWords-1; i > j ; i-- ) - if ( this.data[i] != 0 ) return 1; - }else if ( this.nWords < other.nWords ){ - // if any of other's high-order words is non-zero, - // then the answer is evident - int j = this.nWords-1; - for ( i = other.nWords-1; i > j ; i-- ) - if ( other.data[i] != 0 ) return -1; - } else{ - i = this.nWords-1; - } - for ( ; i > 0 ; i-- ) - if ( this.data[i] != other.data[i] ) - break; - // careful! want unsigned compare! - // use brute force here. - int a = this.data[i]; - int b = other.data[i]; - if ( a < 0 ){ - // a is really big, unsigned - if ( b < 0 ){ - return a-b; // both big, negative - } else { - return 1; // b not big, answer is obvious; - } - } else { - // a is not really big - if ( b < 0 ) { - // but b is really big - return -1; - } else { - return a - b; - } - } - } - - /* - * Compute - * q = (int)( this / S ) - * this = 10 * ( this mod S ) - * Return q. - * This is the iteration step of digit development for output. - * We assume that S has been normalized, as above, and that - * "this" has been lshift'ed accordingly. - * Also assume, of course, that the result, q, can be expressed - * as an integer, 0 <= q < 10. - */ - public int - quoRemIteration( OldFDBigIntForTest S )throws IllegalArgumentException { - // ensure that this and S have the same number of - // digits. If S is properly normalized and q < 10 then - // this must be so. - if ( nWords != S.nWords ){ - throw new IllegalArgumentException("disparate values"); - } - // estimate q the obvious way. We will usually be - // right. If not, then we're only off by a little and - // will re-add. - int n = nWords-1; - long q = ((long)data[n]&0xffffffffL) / (long)S.data[n]; - long diff = 0L; - for ( int i = 0; i <= n ; i++ ){ - diff += ((long)data[i]&0xffffffffL) - q*((long)S.data[i]&0xffffffffL); - data[i] = (int)diff; - diff >>= 32; // N.B. SIGNED shift. - } - if ( diff != 0L ) { - // damn, damn, damn. q is too big. - // add S back in until this turns +. This should - // not be very many times! - long sum = 0L; - while ( sum == 0L ){ - sum = 0L; - for ( int i = 0; i <= n; i++ ){ - sum += ((long)data[i]&0xffffffffL) + ((long)S.data[i]&0xffffffffL); - data[i] = (int) sum; - sum >>= 32; // Signed or unsigned, answer is 0 or 1 - } - /* - * Originally the following line read - * "if ( sum !=0 && sum != -1 )" - * but that would be wrong, because of the - * treatment of the two values as entirely unsigned, - * it would be impossible for a carry-out to be interpreted - * as -1 -- it would have to be a single-bit carry-out, or - * +1. - */ - assert sum == 0 || sum == 1 : sum; // carry out of division correction - q -= 1; - } - } - // finally, we can multiply this by 10. - // it cannot overflow, right, as the high-order word has - // at least 4 high-order zeros! - long p = 0L; - for ( int i = 0; i <= n; i++ ){ - p += 10*((long)data[i]&0xffffffffL); - data[i] = (int)p; - p >>= 32; // SIGNED shift. - } - assert p == 0L : p; // Carry out of *10 - return (int)q; - } - - public long - longValue(){ - // if this can be represented as a long, return the value - assert this.nWords > 0 : this.nWords; // longValue confused - - if (this.nWords == 1) - return ((long)data[0]&0xffffffffL); - - assert dataInRangeIsZero(2, this.nWords, this); // value too big - assert data[1] >= 0; // value too big - return ((long)(data[1]) << 32) | ((long)data[0]&0xffffffffL); - } - - public String - toString() { - StringBuffer r = new StringBuffer(30); - r.append('['); - int i = Math.min( nWords-1, data.length-1) ; - if ( nWords > data.length ){ - r.append( "("+data.length+"<"+nWords+"!)" ); - } - for( ; i> 0 ; i-- ){ - r.append( Integer.toHexString( data[i] ) ); - r.append(' '); - } - r.append( Integer.toHexString( data[0] ) ); - r.append(']'); - return new String( r ); - } -} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/sun/misc/FloatingDecimal/OldFloatingDecimalForTest.java --- a/jdk/test/sun/misc/FloatingDecimal/OldFloatingDecimalForTest.java Wed Dec 23 15:41:50 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2434 +0,0 @@ -/* - * Copyright (c) 1996, 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 sun.misc; - -import java.util.regex.*; - -public class OldFloatingDecimalForTest{ - boolean isExceptional; - boolean isNegative; - int decExponent; - char digits[]; - int nDigits; - int bigIntExp; - int bigIntNBits; - boolean mustSetRoundDir = false; - boolean fromHex = false; - int roundDir = 0; // set by doubleValue - - /* - * The fields below provides additional information about the result of - * the binary to decimal digits conversion done in dtoa() and roundup() - * methods. They are changed if needed by those two methods. - */ - - // True if the dtoa() binary to decimal conversion was exact. - boolean exactDecimalConversion = false; - - // True if the result of the binary to decimal conversion was rounded-up - // at the end of the conversion process, i.e. roundUp() method was called. - boolean decimalDigitsRoundedUp = false; - - private OldFloatingDecimalForTest( boolean negSign, int decExponent, char []digits, int n, boolean e ) - { - isNegative = negSign; - isExceptional = e; - this.decExponent = decExponent; - this.digits = digits; - this.nDigits = n; - } - - /* - * Constants of the implementation - * Most are IEEE-754 related. - * (There are more really boring constants at the end.) - */ - static final long signMask = 0x8000000000000000L; - static final long expMask = 0x7ff0000000000000L; - static final long fractMask= ~(signMask|expMask); - static final int expShift = 52; - static final int expBias = 1023; - static final long fractHOB = ( 1L< 0L ) { // i.e. while ((v&highbit) == 0L ) - v <<= 1; - } - - int n = 0; - while (( v & lowbytes ) != 0L ){ - v <<= 8; - n += 8; - } - while ( v != 0L ){ - v <<= 1; - n += 1; - } - return n; - } - - /* - * Keep big powers of 5 handy for future reference. - */ - private static OldFDBigIntForTest b5p[]; - - private static synchronized OldFDBigIntForTest - big5pow( int p ){ - assert p >= 0 : p; // negative power of 5 - if ( b5p == null ){ - b5p = new OldFDBigIntForTest[ p+1 ]; - }else if (b5p.length <= p ){ - OldFDBigIntForTest t[] = new OldFDBigIntForTest[ p+1 ]; - System.arraycopy( b5p, 0, t, 0, b5p.length ); - b5p = t; - } - if ( b5p[p] != null ) - return b5p[p]; - else if ( p < small5pow.length ) - return b5p[p] = new OldFDBigIntForTest( small5pow[p] ); - else if ( p < long5pow.length ) - return b5p[p] = new OldFDBigIntForTest( long5pow[p] ); - else { - // construct the value. - // recursively. - int q, r; - // in order to compute 5^p, - // compute its square root, 5^(p/2) and square. - // or, let q = p / 2, r = p -q, then - // 5^p = 5^(q+r) = 5^q * 5^r - q = p >> 1; - r = p - q; - OldFDBigIntForTest bigq = b5p[q]; - if ( bigq == null ) - bigq = big5pow ( q ); - if ( r < small5pow.length ){ - return (b5p[p] = bigq.mult( small5pow[r] ) ); - }else{ - OldFDBigIntForTest bigr = b5p[ r ]; - if ( bigr == null ) - bigr = big5pow( r ); - return (b5p[p] = bigq.mult( bigr ) ); - } - } - } - - // - // a common operation - // - private static OldFDBigIntForTest - multPow52( OldFDBigIntForTest v, int p5, int p2 ){ - if ( p5 != 0 ){ - if ( p5 < small5pow.length ){ - v = v.mult( small5pow[p5] ); - } else { - v = v.mult( big5pow( p5 ) ); - } - } - if ( p2 != 0 ){ - v.lshiftMe( p2 ); - } - return v; - } - - // - // another common operation - // - private static OldFDBigIntForTest - constructPow52( int p5, int p2 ){ - OldFDBigIntForTest v = new OldFDBigIntForTest( big5pow( p5 ) ); - if ( p2 != 0 ){ - v.lshiftMe( p2 ); - } - return v; - } - - /* - * Make a floating double into a OldFDBigIntForTest. - * This could also be structured as a OldFDBigIntForTest - * constructor, but we'd have to build a lot of knowledge - * about floating-point representation into it, and we don't want to. - * - * AS A SIDE EFFECT, THIS METHOD WILL SET THE INSTANCE VARIABLES - * bigIntExp and bigIntNBits - * - */ - private OldFDBigIntForTest - doubleToBigInt( double dval ){ - long lbits = Double.doubleToLongBits( dval ) & ~signMask; - int binexp = (int)(lbits >>> expShift); - lbits &= fractMask; - if ( binexp > 0 ){ - lbits |= fractHOB; - } else { - assert lbits != 0L : lbits; // doubleToBigInt(0.0) - binexp +=1; - while ( (lbits & fractHOB ) == 0L){ - lbits <<= 1; - binexp -= 1; - } - } - binexp -= expBias; - int nbits = countBits( lbits ); - /* - * We now know where the high-order 1 bit is, - * and we know how many there are. - */ - int lowOrderZeros = expShift+1-nbits; - lbits >>>= lowOrderZeros; - - bigIntExp = binexp+1-nbits; - bigIntNBits = nbits; - return new OldFDBigIntForTest( lbits ); - } - - /* - * Compute a number that is the ULP of the given value, - * for purposes of addition/subtraction. Generally easy. - * More difficult if subtracting and the argument - * is a normalized a power of 2, as the ULP changes at these points. - */ - private static double ulp( double dval, boolean subtracting ){ - long lbits = Double.doubleToLongBits( dval ) & ~signMask; - int binexp = (int)(lbits >>> expShift); - double ulpval; - if ( subtracting && ( binexp >= expShift ) && ((lbits&fractMask) == 0L) ){ - // for subtraction from normalized, powers of 2, - // use next-smaller exponent - binexp -= 1; - } - if ( binexp > expShift ){ - ulpval = Double.longBitsToDouble( ((long)(binexp-expShift))< 0L (not zero, nor negative). - * - * The only reason that we develop the digits here, rather than - * calling on Long.toString() is that we can do it a little faster, - * and besides want to treat trailing 0s specially. If Long.toString - * changes, we should re-evaluate this strategy! - */ - private void - developLongDigits( int decExponent, long lvalue, long insignificant ){ - char digits[]; - int ndigits; - int digitno; - int c; - // - // Discard non-significant low-order bits, while rounding, - // up to insignificant value. - int i; - for ( i = 0; insignificant >= 10L; i++ ) - insignificant /= 10L; - if ( i != 0 ){ - long pow10 = long5pow[i] << i; // 10^i == 5^i * 2^i; - long residue = lvalue % pow10; - lvalue /= pow10; - decExponent += i; - if ( residue >= (pow10>>1) ){ - // round up based on the low-order bits we're discarding - lvalue++; - } - } - if ( lvalue <= Integer.MAX_VALUE ){ - assert lvalue > 0L : lvalue; // lvalue <= 0 - // even easier subcase! - // can do int arithmetic rather than long! - int ivalue = (int)lvalue; - ndigits = 10; - digits = perThreadBuffer.get(); - digitno = ndigits-1; - c = ivalue%10; - ivalue /= 10; - while ( c == 0 ){ - decExponent++; - c = ivalue%10; - ivalue /= 10; - } - while ( ivalue != 0){ - digits[digitno--] = (char)(c+'0'); - decExponent++; - c = ivalue%10; - ivalue /= 10; - } - digits[digitno] = (char)(c+'0'); - } else { - // same algorithm as above (same bugs, too ) - // but using long arithmetic. - ndigits = 20; - digits = perThreadBuffer.get(); - digitno = ndigits-1; - c = (int)(lvalue%10L); - lvalue /= 10L; - while ( c == 0 ){ - decExponent++; - c = (int)(lvalue%10L); - lvalue /= 10L; - } - while ( lvalue != 0L ){ - digits[digitno--] = (char)(c+'0'); - decExponent++; - c = (int)(lvalue%10L); - lvalue /= 10; - } - digits[digitno] = (char)(c+'0'); - } - char result []; - ndigits -= digitno; - result = new char[ ndigits ]; - System.arraycopy( digits, digitno, result, 0, ndigits ); - this.digits = result; - this.decExponent = decExponent+1; - this.nDigits = ndigits; - } - - // - // add one to the least significant digit. - // in the unlikely event there is a carry out, - // deal with it. - // assert that this will only happen where there - // is only one digit, e.g. (float)1e-44 seems to do it. - // - private void - roundup(){ - int i; - int q = digits[ i = (nDigits-1)]; - if ( q == '9' ){ - while ( q == '9' && i > 0 ){ - digits[i] = '0'; - q = digits[--i]; - } - if ( q == '9' ){ - // carryout! High-order 1, rest 0s, larger exp. - decExponent += 1; - digits[0] = '1'; - return; - } - // else fall through. - } - digits[i] = (char)(q+1); - decimalDigitsRoundedUp = true; - } - - public boolean digitsRoundedUp() { - return decimalDigitsRoundedUp; - } - - /* - * FIRST IMPORTANT CONSTRUCTOR: DOUBLE - */ - public OldFloatingDecimalForTest( double d ) - { - long dBits = Double.doubleToLongBits( d ); - long fractBits; - int binExp; - int nSignificantBits; - - // discover and delete sign - if ( (dBits&signMask) != 0 ){ - isNegative = true; - dBits ^= signMask; - } else { - isNegative = false; - } - // Begin to unpack - // Discover obvious special cases of NaN and Infinity. - binExp = (int)( (dBits&expMask) >> expShift ); - fractBits = dBits&fractMask; - if ( binExp == (int)(expMask>>expShift) ) { - isExceptional = true; - if ( fractBits == 0L ){ - digits = infinity; - } else { - digits = notANumber; - isNegative = false; // NaN has no sign! - } - nDigits = digits.length; - return; - } - isExceptional = false; - // Finish unpacking - // Normalize denormalized numbers. - // Insert assumed high-order bit for normalized numbers. - // Subtract exponent bias. - if ( binExp == 0 ){ - if ( fractBits == 0L ){ - // not a denorm, just a 0! - decExponent = 0; - digits = zero; - nDigits = 1; - return; - } - while ( (fractBits&fractHOB) == 0L ){ - fractBits <<= 1; - binExp -= 1; - } - nSignificantBits = expShift + binExp +1; // recall binExp is - shift count. - binExp += 1; - } else { - fractBits |= fractHOB; - nSignificantBits = expShift+1; - } - binExp -= expBias; - // call the routine that actually does all the hard work. - dtoa( binExp, fractBits, nSignificantBits ); - } - - /* - * SECOND IMPORTANT CONSTRUCTOR: SINGLE - */ - public OldFloatingDecimalForTest( float f ) - { - int fBits = Float.floatToIntBits( f ); - int fractBits; - int binExp; - int nSignificantBits; - - // discover and delete sign - if ( (fBits&singleSignMask) != 0 ){ - isNegative = true; - fBits ^= singleSignMask; - } else { - isNegative = false; - } - // Begin to unpack - // Discover obvious special cases of NaN and Infinity. - binExp = (fBits&singleExpMask) >> singleExpShift; - fractBits = fBits&singleFractMask; - if ( binExp == (singleExpMask>>singleExpShift) ) { - isExceptional = true; - if ( fractBits == 0L ){ - digits = infinity; - } else { - digits = notANumber; - isNegative = false; // NaN has no sign! - } - nDigits = digits.length; - return; - } - isExceptional = false; - // Finish unpacking - // Normalize denormalized numbers. - // Insert assumed high-order bit for normalized numbers. - // Subtract exponent bias. - if ( binExp == 0 ){ - if ( fractBits == 0 ){ - // not a denorm, just a 0! - decExponent = 0; - digits = zero; - nDigits = 1; - return; - } - while ( (fractBits&singleFractHOB) == 0 ){ - fractBits <<= 1; - binExp -= 1; - } - nSignificantBits = singleExpShift + binExp +1; // recall binExp is - shift count. - binExp += 1; - } else { - fractBits |= singleFractHOB; - nSignificantBits = singleExpShift+1; - } - binExp -= singleExpBias; - // call the routine that actually does all the hard work. - dtoa( binExp, ((long)fractBits)<<(expShift-singleExpShift), nSignificantBits ); - } - - private void - dtoa( int binExp, long fractBits, int nSignificantBits ) - { - int nFractBits; // number of significant bits of fractBits; - int nTinyBits; // number of these to the right of the point. - int decExp; - - // Examine number. Determine if it is an easy case, - // which we can do pretty trivially using float/long conversion, - // or whether we must do real work. - nFractBits = countBits( fractBits ); - nTinyBits = Math.max( 0, nFractBits - binExp - 1 ); - if ( binExp <= maxSmallBinExp && binExp >= minSmallBinExp ){ - // Look more closely at the number to decide if, - // with scaling by 10^nTinyBits, the result will fit in - // a long. - if ( (nTinyBits < long5pow.length) && ((nFractBits + n5bits[nTinyBits]) < 64 ) ){ - /* - * We can do this: - * take the fraction bits, which are normalized. - * (a) nTinyBits == 0: Shift left or right appropriately - * to align the binary point at the extreme right, i.e. - * where a long int point is expected to be. The integer - * result is easily converted to a string. - * (b) nTinyBits > 0: Shift right by expShift-nFractBits, - * which effectively converts to long and scales by - * 2^nTinyBits. Then multiply by 5^nTinyBits to - * complete the scaling. We know this won't overflow - * because we just counted the number of bits necessary - * in the result. The integer you get from this can - * then be converted to a string pretty easily. - */ - long halfULP; - if ( nTinyBits == 0 ) { - if ( binExp > nSignificantBits ){ - halfULP = 1L << ( binExp-nSignificantBits-1); - } else { - halfULP = 0L; - } - if ( binExp >= expShift ){ - fractBits <<= (binExp-expShift); - } else { - fractBits >>>= (expShift-binExp) ; - } - developLongDigits( 0, fractBits, halfULP ); - return; - } - /* - * The following causes excess digits to be printed - * out in the single-float case. Our manipulation of - * halfULP here is apparently not correct. If we - * better understand how this works, perhaps we can - * use this special case again. But for the time being, - * we do not. - * else { - * fractBits >>>= expShift+1-nFractBits; - * fractBits *= long5pow[ nTinyBits ]; - * halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits); - * developLongDigits( -nTinyBits, fractBits, halfULP ); - * return; - * } - */ - } - } - /* - * This is the hard case. We are going to compute large positive - * integers B and S and integer decExp, s.t. - * d = ( B / S ) * 10^decExp - * 1 <= B / S < 10 - * Obvious choices are: - * decExp = floor( log10(d) ) - * B = d * 2^nTinyBits * 10^max( 0, -decExp ) - * S = 10^max( 0, decExp) * 2^nTinyBits - * (noting that nTinyBits has already been forced to non-negative) - * I am also going to compute a large positive integer - * M = (1/2^nSignificantBits) * 2^nTinyBits * 10^max( 0, -decExp ) - * i.e. M is (1/2) of the ULP of d, scaled like B. - * When we iterate through dividing B/S and picking off the - * quotient bits, we will know when to stop when the remainder - * is <= M. - * - * We keep track of powers of 2 and powers of 5. - */ - - /* - * Estimate decimal exponent. (If it is small-ish, - * we could double-check.) - * - * First, scale the mantissa bits such that 1 <= d2 < 2. - * We are then going to estimate - * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5) - * and so we can estimate - * log10(d) ~=~ log10(d2) + binExp * log10(2) - * take the floor and call it decExp. - * FIXME -- use more precise constants here. It costs no more. - */ - double d2 = Double.longBitsToDouble( - expOne | ( fractBits &~ fractHOB ) ); - decExp = (int)Math.floor( - (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981 ); - int B2, B5; // powers of 2 and powers of 5, respectively, in B - int S2, S5; // powers of 2 and powers of 5, respectively, in S - int M2, M5; // powers of 2 and powers of 5, respectively, in M - int Bbits; // binary digits needed to represent B, approx. - int tenSbits; // binary digits needed to represent 10*S, approx. - OldFDBigIntForTest Sval, Bval, Mval; - - B5 = Math.max( 0, -decExp ); - B2 = B5 + nTinyBits + binExp; - - S5 = Math.max( 0, decExp ); - S2 = S5 + nTinyBits; - - M5 = B5; - M2 = B2 - nSignificantBits; - - /* - * the long integer fractBits contains the (nFractBits) interesting - * bits from the mantissa of d ( hidden 1 added if necessary) followed - * by (expShift+1-nFractBits) zeros. In the interest of compactness, - * I will shift out those zeros before turning fractBits into a - * OldFDBigIntForTest. The resulting whole number will be - * d * 2^(nFractBits-1-binExp). - */ - fractBits >>>= (expShift+1-nFractBits); - B2 -= nFractBits-1; - int common2factor = Math.min( B2, S2 ); - B2 -= common2factor; - S2 -= common2factor; - M2 -= common2factor; - - /* - * HACK!! For exact powers of two, the next smallest number - * is only half as far away as we think (because the meaning of - * ULP changes at power-of-two bounds) for this reason, we - * hack M2. Hope this works. - */ - if ( nFractBits == 1 ) - M2 -= 1; - - if ( M2 < 0 ){ - // oops. - // since we cannot scale M down far enough, - // we must scale the other values up. - B2 -= M2; - S2 -= M2; - M2 = 0; - } - /* - * Construct, Scale, iterate. - * Some day, we'll write a stopping test that takes - * account of the asymmetry of the spacing of floating-point - * numbers below perfect powers of 2 - * 26 Sept 96 is not that day. - * So we use a symmetric test. - */ - char digits[] = this.digits = new char[18]; - int ndigit = 0; - boolean low, high; - long lowDigitDifference; - int q; - - /* - * Detect the special cases where all the numbers we are about - * to compute will fit in int or long integers. - * In these cases, we will avoid doing OldFDBigIntForTest arithmetic. - * We use the same algorithms, except that we "normalize" - * our OldFDBigIntForTests before iterating. This is to make division easier, - * as it makes our fist guess (quotient of high-order words) - * more accurate! - * - * Some day, we'll write a stopping test that takes - * account of the asymmetry of the spacing of floating-point - * numbers below perfect powers of 2 - * 26 Sept 96 is not that day. - * So we use a symmetric test. - */ - Bbits = nFractBits + B2 + (( B5 < n5bits.length )? n5bits[B5] : ( B5*3 )); - tenSbits = S2+1 + (( (S5+1) < n5bits.length )? n5bits[(S5+1)] : ( (S5+1)*3 )); - if ( Bbits < 64 && tenSbits < 64){ - if ( Bbits < 32 && tenSbits < 32){ - // wa-hoo! They're all ints! - int b = ((int)fractBits * small5pow[B5] ) << B2; - int s = small5pow[S5] << S2; - int m = small5pow[M5] << M2; - int tens = s * 10; - /* - * Unroll the first iteration. If our decExp estimate - * was too high, our first quotient will be zero. In this - * case, we discard it and decrement decExp. - */ - ndigit = 0; - q = b / s; - b = 10 * ( b % s ); - m *= 10; - low = (b < m ); - high = (b+m > tens ); - assert q < 10 : q; // excessively large digit - if ( (q == 0) && ! high ){ - // oops. Usually ignore leading zero. - decExp--; - } else { - digits[ndigit++] = (char)('0' + q); - } - /* - * HACK! Java spec sez that we always have at least - * one digit after the . in either F- or E-form output. - * Thus we will need more than one digit if we're using - * E-form - */ - if ( decExp < -3 || decExp >= 8 ){ - high = low = false; - } - while( ! low && ! high ){ - q = b / s; - b = 10 * ( b % s ); - m *= 10; - assert q < 10 : q; // excessively large digit - if ( m > 0L ){ - low = (b < m ); - high = (b+m > tens ); - } else { - // hack -- m might overflow! - // in this case, it is certainly > b, - // which won't - // and b+m > tens, too, since that has overflowed - // either! - low = true; - high = true; - } - digits[ndigit++] = (char)('0' + q); - } - lowDigitDifference = (b<<1) - tens; - exactDecimalConversion = (b == 0); - } else { - // still good! they're all longs! - long b = (fractBits * long5pow[B5] ) << B2; - long s = long5pow[S5] << S2; - long m = long5pow[M5] << M2; - long tens = s * 10L; - /* - * Unroll the first iteration. If our decExp estimate - * was too high, our first quotient will be zero. In this - * case, we discard it and decrement decExp. - */ - ndigit = 0; - q = (int) ( b / s ); - b = 10L * ( b % s ); - m *= 10L; - low = (b < m ); - high = (b+m > tens ); - assert q < 10 : q; // excessively large digit - if ( (q == 0) && ! high ){ - // oops. Usually ignore leading zero. - decExp--; - } else { - digits[ndigit++] = (char)('0' + q); - } - /* - * HACK! Java spec sez that we always have at least - * one digit after the . in either F- or E-form output. - * Thus we will need more than one digit if we're using - * E-form - */ - if ( decExp < -3 || decExp >= 8 ){ - high = low = false; - } - while( ! low && ! high ){ - q = (int) ( b / s ); - b = 10 * ( b % s ); - m *= 10; - assert q < 10 : q; // excessively large digit - if ( m > 0L ){ - low = (b < m ); - high = (b+m > tens ); - } else { - // hack -- m might overflow! - // in this case, it is certainly > b, - // which won't - // and b+m > tens, too, since that has overflowed - // either! - low = true; - high = true; - } - digits[ndigit++] = (char)('0' + q); - } - lowDigitDifference = (b<<1) - tens; - exactDecimalConversion = (b == 0); - } - } else { - OldFDBigIntForTest ZeroVal = new OldFDBigIntForTest(0); - OldFDBigIntForTest tenSval; - int shiftBias; - - /* - * We really must do OldFDBigIntForTest arithmetic. - * Fist, construct our OldFDBigIntForTest initial values. - */ - Bval = multPow52( new OldFDBigIntForTest( fractBits ), B5, B2 ); - Sval = constructPow52( S5, S2 ); - Mval = constructPow52( M5, M2 ); - - - // normalize so that division works better - Bval.lshiftMe( shiftBias = Sval.normalizeMe() ); - Mval.lshiftMe( shiftBias ); - tenSval = Sval.mult( 10 ); - /* - * Unroll the first iteration. If our decExp estimate - * was too high, our first quotient will be zero. In this - * case, we discard it and decrement decExp. - */ - ndigit = 0; - q = Bval.quoRemIteration( Sval ); - Mval = Mval.mult( 10 ); - low = (Bval.cmp( Mval ) < 0); - high = (Bval.add( Mval ).cmp( tenSval ) > 0 ); - assert q < 10 : q; // excessively large digit - if ( (q == 0) && ! high ){ - // oops. Usually ignore leading zero. - decExp--; - } else { - digits[ndigit++] = (char)('0' + q); - } - /* - * HACK! Java spec sez that we always have at least - * one digit after the . in either F- or E-form output. - * Thus we will need more than one digit if we're using - * E-form - */ - if ( decExp < -3 || decExp >= 8 ){ - high = low = false; - } - while( ! low && ! high ){ - q = Bval.quoRemIteration( Sval ); - Mval = Mval.mult( 10 ); - assert q < 10 : q; // excessively large digit - low = (Bval.cmp( Mval ) < 0); - high = (Bval.add( Mval ).cmp( tenSval ) > 0 ); - digits[ndigit++] = (char)('0' + q); - } - if ( high && low ){ - Bval.lshiftMe(1); - lowDigitDifference = Bval.cmp(tenSval); - } else { - lowDigitDifference = 0L; // this here only for flow analysis! - } - exactDecimalConversion = (Bval.cmp( ZeroVal ) == 0); - } - this.decExponent = decExp+1; - this.digits = digits; - this.nDigits = ndigit; - /* - * Last digit gets rounded based on stopping condition. - */ - if ( high ){ - if ( low ){ - if ( lowDigitDifference == 0L ){ - // it's a tie! - // choose based on which digits we like. - if ( (digits[nDigits-1]&1) != 0 ) roundup(); - } else if ( lowDigitDifference > 0 ){ - roundup(); - } - } else { - roundup(); - } - } - } - - public boolean decimalDigitsExact() { - return exactDecimalConversion; - } - - public String - toString(){ - // most brain-dead version - StringBuffer result = new StringBuffer( nDigits+8 ); - if ( isNegative ){ result.append( '-' ); } - if ( isExceptional ){ - result.append( digits, 0, nDigits ); - } else { - result.append( "0."); - result.append( digits, 0, nDigits ); - result.append('e'); - result.append( decExponent ); - } - return new String(result); - } - - public String toJavaFormatString() { - char result[] = perThreadBuffer.get(); - int i = getChars(result); - return new String(result, 0, i); - } - - private int getChars(char[] result) { - assert nDigits <= 19 : nDigits; // generous bound on size of nDigits - int i = 0; - if (isNegative) { result[0] = '-'; i = 1; } - if (isExceptional) { - System.arraycopy(digits, 0, result, i, nDigits); - i += nDigits; - } else { - if (decExponent > 0 && decExponent < 8) { - // print digits.digits. - int charLength = Math.min(nDigits, decExponent); - System.arraycopy(digits, 0, result, i, charLength); - i += charLength; - if (charLength < decExponent) { - charLength = decExponent-charLength; - System.arraycopy(zero, 0, result, i, charLength); - i += charLength; - result[i++] = '.'; - result[i++] = '0'; - } else { - result[i++] = '.'; - if (charLength < nDigits) { - int t = nDigits - charLength; - System.arraycopy(digits, charLength, result, i, t); - i += t; - } else { - result[i++] = '0'; - } - } - } else if (decExponent <=0 && decExponent > -3) { - result[i++] = '0'; - result[i++] = '.'; - if (decExponent != 0) { - System.arraycopy(zero, 0, result, i, -decExponent); - i -= decExponent; - } - System.arraycopy(digits, 0, result, i, nDigits); - i += nDigits; - } else { - result[i++] = digits[0]; - result[i++] = '.'; - if (nDigits > 1) { - System.arraycopy(digits, 1, result, i, nDigits-1); - i += nDigits-1; - } else { - result[i++] = '0'; - } - result[i++] = 'E'; - int e; - if (decExponent <= 0) { - result[i++] = '-'; - e = -decExponent+1; - } else { - e = decExponent-1; - } - // decExponent has 1, 2, or 3, digits - if (e <= 9) { - result[i++] = (char)(e+'0'); - } else if (e <= 99) { - result[i++] = (char)(e/10 +'0'); - result[i++] = (char)(e%10 + '0'); - } else { - result[i++] = (char)(e/100+'0'); - e %= 100; - result[i++] = (char)(e/10+'0'); - result[i++] = (char)(e%10 + '0'); - } - } - } - return i; - } - - // Per-thread buffer for string/stringbuffer conversion - private static ThreadLocal perThreadBuffer = new ThreadLocal() { - protected synchronized char[] initialValue() { - return new char[26]; - } - }; - - public void appendTo(Appendable buf) { - char result[] = perThreadBuffer.get(); - int i = getChars(result); - if (buf instanceof StringBuilder) - ((StringBuilder) buf).append(result, 0, i); - else if (buf instanceof StringBuffer) - ((StringBuffer) buf).append(result, 0, i); - else - assert false; - } - - @SuppressWarnings("fallthrough") - public static OldFloatingDecimalForTest - readJavaFormatString( String in ) throws NumberFormatException { - boolean isNegative = false; - boolean signSeen = false; - int decExp; - char c; - - parseNumber: - try{ - in = in.trim(); // don't fool around with white space. - // throws NullPointerException if null - int l = in.length(); - if ( l == 0 ) throw new NumberFormatException("empty String"); - int i = 0; - switch ( c = in.charAt( i ) ){ - case '-': - isNegative = true; - //FALLTHROUGH - case '+': - i++; - signSeen = true; - } - - // Check for NaN and Infinity strings - c = in.charAt(i); - if(c == 'N' || c == 'I') { // possible NaN or infinity - boolean potentialNaN = false; - char targetChars[] = null; // char array of "NaN" or "Infinity" - - if(c == 'N') { - targetChars = notANumber; - potentialNaN = true; - } else { - targetChars = infinity; - } - - // compare Input string to "NaN" or "Infinity" - int j = 0; - while(i < l && j < targetChars.length) { - if(in.charAt(i) == targetChars[j]) { - i++; j++; - } - else // something is amiss, throw exception - break parseNumber; - } - - // For the candidate string to be a NaN or infinity, - // all characters in input string and target char[] - // must be matched ==> j must equal targetChars.length - // and i must equal l - if( (j == targetChars.length) && (i == l) ) { // return NaN or infinity - return (potentialNaN ? new OldFloatingDecimalForTest(Double.NaN) // NaN has no sign - : new OldFloatingDecimalForTest(isNegative? - Double.NEGATIVE_INFINITY: - Double.POSITIVE_INFINITY)) ; - } - else { // something went wrong, throw exception - break parseNumber; - } - - } else if (c == '0') { // check for hexadecimal floating-point number - if (l > i+1 ) { - char ch = in.charAt(i+1); - if (ch == 'x' || ch == 'X' ) // possible hex string - return parseHexString(in); - } - } // look for and process decimal floating-point string - - char[] digits = new char[ l ]; - int nDigits= 0; - boolean decSeen = false; - int decPt = 0; - int nLeadZero = 0; - int nTrailZero= 0; - digitLoop: - while ( i < l ){ - switch ( c = in.charAt( i ) ){ - case '0': - if ( nDigits > 0 ){ - nTrailZero += 1; - } else { - nLeadZero += 1; - } - break; // out of switch. - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - while ( nTrailZero > 0 ){ - digits[nDigits++] = '0'; - nTrailZero -= 1; - } - digits[nDigits++] = c; - break; // out of switch. - case '.': - if ( decSeen ){ - // already saw one ., this is the 2nd. - throw new NumberFormatException("multiple points"); - } - decPt = i; - if ( signSeen ){ - decPt -= 1; - } - decSeen = true; - break; // out of switch. - default: - break digitLoop; - } - i++; - } - /* - * At this point, we've scanned all the digits and decimal - * point we're going to see. Trim off leading and trailing - * zeros, which will just confuse us later, and adjust - * our initial decimal exponent accordingly. - * To review: - * we have seen i total characters. - * nLeadZero of them were zeros before any other digits. - * nTrailZero of them were zeros after any other digits. - * if ( decSeen ), then a . was seen after decPt characters - * ( including leading zeros which have been discarded ) - * nDigits characters were neither lead nor trailing - * zeros, nor point - */ - /* - * special hack: if we saw no non-zero digits, then the - * answer is zero! - * Unfortunately, we feel honor-bound to keep parsing! - */ - if ( nDigits == 0 ){ - digits = zero; - nDigits = 1; - if ( nLeadZero == 0 ){ - // we saw NO DIGITS AT ALL, - // not even a crummy 0! - // this is not allowed. - break parseNumber; // go throw exception - } - - } - - /* Our initial exponent is decPt, adjusted by the number of - * discarded zeros. Or, if there was no decPt, - * then its just nDigits adjusted by discarded trailing zeros. - */ - if ( decSeen ){ - decExp = decPt - nLeadZero; - } else { - decExp = nDigits+nTrailZero; - } - - /* - * Look for 'e' or 'E' and an optionally signed integer. - */ - if ( (i < l) && (((c = in.charAt(i) )=='e') || (c == 'E') ) ){ - int expSign = 1; - int expVal = 0; - int reallyBig = Integer.MAX_VALUE / 10; - boolean expOverflow = false; - switch( in.charAt(++i) ){ - case '-': - expSign = -1; - //FALLTHROUGH - case '+': - i++; - } - int expAt = i; - expLoop: - while ( i < l ){ - if ( expVal >= reallyBig ){ - // the next character will cause integer - // overflow. - expOverflow = true; - } - switch ( c = in.charAt(i++) ){ - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - expVal = expVal*10 + ( (int)c - (int)'0' ); - continue; - default: - i--; // back up. - break expLoop; // stop parsing exponent. - } - } - int expLimit = bigDecimalExponent+nDigits+nTrailZero; - if ( expOverflow || ( expVal > expLimit ) ){ - // - // The intent here is to end up with - // infinity or zero, as appropriate. - // The reason for yielding such a small decExponent, - // rather than something intuitive such as - // expSign*Integer.MAX_VALUE, is that this value - // is subject to further manipulation in - // doubleValue() and floatValue(), and I don't want - // it to be able to cause overflow there! - // (The only way we can get into trouble here is for - // really outrageous nDigits+nTrailZero, such as 2 billion. ) - // - decExp = expSign*expLimit; - } else { - // this should not overflow, since we tested - // for expVal > (MAX+N), where N >= abs(decExp) - decExp = decExp + expSign*expVal; - } - - // if we saw something not a digit ( or end of string ) - // after the [Ee][+-], without seeing any digits at all - // this is certainly an error. If we saw some digits, - // but then some trailing garbage, that might be ok. - // so we just fall through in that case. - // HUMBUG - if ( i == expAt ) - break parseNumber; // certainly bad - } - /* - * We parsed everything we could. - * If there are leftovers, then this is not good input! - */ - if ( i < l && - ((i != l - 1) || - (in.charAt(i) != 'f' && - in.charAt(i) != 'F' && - in.charAt(i) != 'd' && - in.charAt(i) != 'D'))) { - break parseNumber; // go throw exception - } - - return new OldFloatingDecimalForTest( isNegative, decExp, digits, nDigits, false ); - } catch ( StringIndexOutOfBoundsException e ){ } - throw new NumberFormatException("For input string: \"" + in + "\""); - } - - /* - * Take a FloatingDecimal, which we presumably just scanned in, - * and find out what its value is, as a double. - * - * AS A SIDE EFFECT, SET roundDir TO INDICATE PREFERRED - * ROUNDING DIRECTION in case the result is really destined - * for a single-precision float. - */ - - public strictfp double doubleValue(){ - int kDigits = Math.min( nDigits, maxDecimalDigits+1 ); - long lValue; - double dValue; - double rValue, tValue; - - // First, check for NaN and Infinity values - if(digits == infinity || digits == notANumber) { - if(digits == notANumber) - return Double.NaN; - else - return (isNegative?Double.NEGATIVE_INFINITY:Double.POSITIVE_INFINITY); - } - else { - if (mustSetRoundDir) { - roundDir = 0; - } - /* - * convert the lead kDigits to a long integer. - */ - // (special performance hack: start to do it using int) - int iValue = (int)digits[0]-(int)'0'; - int iDigits = Math.min( kDigits, intDecimalDigits ); - for ( int i=1; i < iDigits; i++ ){ - iValue = iValue*10 + (int)digits[i]-(int)'0'; - } - lValue = (long)iValue; - for ( int i=iDigits; i < kDigits; i++ ){ - lValue = lValue*10L + (long)((int)digits[i]-(int)'0'); - } - dValue = (double)lValue; - int exp = decExponent-kDigits; - /* - * lValue now contains a long integer with the value of - * the first kDigits digits of the number. - * dValue contains the (double) of the same. - */ - - if ( nDigits <= maxDecimalDigits ){ - /* - * possibly an easy case. - * We know that the digits can be represented - * exactly. And if the exponent isn't too outrageous, - * the whole thing can be done with one operation, - * thus one rounding error. - * Note that all our constructors trim all leading and - * trailing zeros, so simple values (including zero) - * will always end up here - */ - if (exp == 0 || dValue == 0.0) - return (isNegative)? -dValue : dValue; // small floating integer - else if ( exp >= 0 ){ - if ( exp <= maxSmallTen ){ - /* - * Can get the answer with one operation, - * thus one roundoff. - */ - rValue = dValue * small10pow[exp]; - if ( mustSetRoundDir ){ - tValue = rValue / small10pow[exp]; - roundDir = ( tValue == dValue ) ? 0 - :( tValue < dValue ) ? 1 - : -1; - } - return (isNegative)? -rValue : rValue; - } - int slop = maxDecimalDigits - kDigits; - if ( exp <= maxSmallTen+slop ){ - /* - * We can multiply dValue by 10^(slop) - * and it is still "small" and exact. - * Then we can multiply by 10^(exp-slop) - * with one rounding. - */ - dValue *= small10pow[slop]; - rValue = dValue * small10pow[exp-slop]; - - if ( mustSetRoundDir ){ - tValue = rValue / small10pow[exp-slop]; - roundDir = ( tValue == dValue ) ? 0 - :( tValue < dValue ) ? 1 - : -1; - } - return (isNegative)? -rValue : rValue; - } - /* - * Else we have a hard case with a positive exp. - */ - } else { - if ( exp >= -maxSmallTen ){ - /* - * Can get the answer in one division. - */ - rValue = dValue / small10pow[-exp]; - tValue = rValue * small10pow[-exp]; - if ( mustSetRoundDir ){ - roundDir = ( tValue == dValue ) ? 0 - :( tValue < dValue ) ? 1 - : -1; - } - return (isNegative)? -rValue : rValue; - } - /* - * Else we have a hard case with a negative exp. - */ - } - } - - /* - * Harder cases: - * The sum of digits plus exponent is greater than - * what we think we can do with one error. - * - * Start by approximating the right answer by, - * naively, scaling by powers of 10. - */ - if ( exp > 0 ){ - if ( decExponent > maxDecimalExponent+1 ){ - /* - * Lets face it. This is going to be - * Infinity. Cut to the chase. - */ - return (isNegative)? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; - } - if ( (exp&15) != 0 ){ - dValue *= small10pow[exp&15]; - } - if ( (exp>>=4) != 0 ){ - int j; - for( j = 0; exp > 1; j++, exp>>=1 ){ - if ( (exp&1)!=0) - dValue *= big10pow[j]; - } - /* - * The reason for the weird exp > 1 condition - * in the above loop was so that the last multiply - * would get unrolled. We handle it here. - * It could overflow. - */ - double t = dValue * big10pow[j]; - if ( Double.isInfinite( t ) ){ - /* - * It did overflow. - * Look more closely at the result. - * If the exponent is just one too large, - * then use the maximum finite as our estimate - * value. Else call the result infinity - * and punt it. - * ( I presume this could happen because - * rounding forces the result here to be - * an ULP or two larger than - * Double.MAX_VALUE ). - */ - t = dValue / 2.0; - t *= big10pow[j]; - if ( Double.isInfinite( t ) ){ - return (isNegative)? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; - } - t = Double.MAX_VALUE; - } - dValue = t; - } - } else if ( exp < 0 ){ - exp = -exp; - if ( decExponent < minDecimalExponent-1 ){ - /* - * Lets face it. This is going to be - * zero. Cut to the chase. - */ - return (isNegative)? -0.0 : 0.0; - } - if ( (exp&15) != 0 ){ - dValue /= small10pow[exp&15]; - } - if ( (exp>>=4) != 0 ){ - int j; - for( j = 0; exp > 1; j++, exp>>=1 ){ - if ( (exp&1)!=0) - dValue *= tiny10pow[j]; - } - /* - * The reason for the weird exp > 1 condition - * in the above loop was so that the last multiply - * would get unrolled. We handle it here. - * It could underflow. - */ - double t = dValue * tiny10pow[j]; - if ( t == 0.0 ){ - /* - * It did underflow. - * Look more closely at the result. - * If the exponent is just one too small, - * then use the minimum finite as our estimate - * value. Else call the result 0.0 - * and punt it. - * ( I presume this could happen because - * rounding forces the result here to be - * an ULP or two less than - * Double.MIN_VALUE ). - */ - t = dValue * 2.0; - t *= tiny10pow[j]; - if ( t == 0.0 ){ - return (isNegative)? -0.0 : 0.0; - } - t = Double.MIN_VALUE; - } - dValue = t; - } - } - - /* - * dValue is now approximately the result. - * The hard part is adjusting it, by comparison - * with OldFDBigIntForTest arithmetic. - * Formulate the EXACT big-number result as - * bigD0 * 10^exp - */ - OldFDBigIntForTest bigD0 = new OldFDBigIntForTest( lValue, digits, kDigits, nDigits ); - exp = decExponent - nDigits; - - correctionLoop: - while(true){ - /* AS A SIDE EFFECT, THIS METHOD WILL SET THE INSTANCE VARIABLES - * bigIntExp and bigIntNBits - */ - OldFDBigIntForTest bigB = doubleToBigInt( dValue ); - - /* - * Scale bigD, bigB appropriately for - * big-integer operations. - * Naively, we multiply by powers of ten - * and powers of two. What we actually do - * is keep track of the powers of 5 and - * powers of 2 we would use, then factor out - * common divisors before doing the work. - */ - int B2, B5; // powers of 2, 5 in bigB - int D2, D5; // powers of 2, 5 in bigD - int Ulp2; // powers of 2 in halfUlp. - if ( exp >= 0 ){ - B2 = B5 = 0; - D2 = D5 = exp; - } else { - B2 = B5 = -exp; - D2 = D5 = 0; - } - if ( bigIntExp >= 0 ){ - B2 += bigIntExp; - } else { - D2 -= bigIntExp; - } - Ulp2 = B2; - // shift bigB and bigD left by a number s. t. - // halfUlp is still an integer. - int hulpbias; - if ( bigIntExp+bigIntNBits <= -expBias+1 ){ - // This is going to be a denormalized number - // (if not actually zero). - // half an ULP is at 2^-(expBias+expShift+1) - hulpbias = bigIntExp+ expBias + expShift; - } else { - hulpbias = expShift + 2 - bigIntNBits; - } - B2 += hulpbias; - D2 += hulpbias; - // if there are common factors of 2, we might just as well - // factor them out, as they add nothing useful. - int common2 = Math.min( B2, Math.min( D2, Ulp2 ) ); - B2 -= common2; - D2 -= common2; - Ulp2 -= common2; - // do multiplications by powers of 5 and 2 - bigB = multPow52( bigB, B5, B2 ); - OldFDBigIntForTest bigD = multPow52( new OldFDBigIntForTest( bigD0 ), D5, D2 ); - // - // to recap: - // bigB is the scaled-big-int version of our floating-point - // candidate. - // bigD is the scaled-big-int version of the exact value - // as we understand it. - // halfUlp is 1/2 an ulp of bigB, except for special cases - // of exact powers of 2 - // - // the plan is to compare bigB with bigD, and if the difference - // is less than halfUlp, then we're satisfied. Otherwise, - // use the ratio of difference to halfUlp to calculate a fudge - // factor to add to the floating value, then go 'round again. - // - OldFDBigIntForTest diff; - int cmpResult; - boolean overvalue; - if ( (cmpResult = bigB.cmp( bigD ) ) > 0 ){ - overvalue = true; // our candidate is too big. - diff = bigB.sub( bigD ); - if ( (bigIntNBits == 1) && (bigIntExp > -expBias+1) ){ - // candidate is a normalized exact power of 2 and - // is too big. We will be subtracting. - // For our purposes, ulp is the ulp of the - // next smaller range. - Ulp2 -= 1; - if ( Ulp2 < 0 ){ - // rats. Cannot de-scale ulp this far. - // must scale diff in other direction. - Ulp2 = 0; - diff.lshiftMe( 1 ); - } - } - } else if ( cmpResult < 0 ){ - overvalue = false; // our candidate is too small. - diff = bigD.sub( bigB ); - } else { - // the candidate is exactly right! - // this happens with surprising frequency - break correctionLoop; - } - OldFDBigIntForTest halfUlp = constructPow52( B5, Ulp2 ); - if ( (cmpResult = diff.cmp( halfUlp ) ) < 0 ){ - // difference is small. - // this is close enough - if (mustSetRoundDir) { - roundDir = overvalue ? -1 : 1; - } - break correctionLoop; - } else if ( cmpResult == 0 ){ - // difference is exactly half an ULP - // round to some other value maybe, then finish - dValue += 0.5*ulp( dValue, overvalue ); - // should check for bigIntNBits == 1 here?? - if (mustSetRoundDir) { - roundDir = overvalue ? -1 : 1; - } - break correctionLoop; - } else { - // difference is non-trivial. - // could scale addend by ratio of difference to - // halfUlp here, if we bothered to compute that difference. - // Most of the time ( I hope ) it is about 1 anyway. - dValue += ulp( dValue, overvalue ); - if ( dValue == 0.0 || dValue == Double.POSITIVE_INFINITY ) - break correctionLoop; // oops. Fell off end of range. - continue; // try again. - } - - } - return (isNegative)? -dValue : dValue; - } - } - - /* - * Take a FloatingDecimal, which we presumably just scanned in, - * and find out what its value is, as a float. - * This is distinct from doubleValue() to avoid the extremely - * unlikely case of a double rounding error, wherein the conversion - * to double has one rounding error, and the conversion of that double - * to a float has another rounding error, IN THE WRONG DIRECTION, - * ( because of the preference to a zero low-order bit ). - */ - - public strictfp float floatValue(){ - int kDigits = Math.min( nDigits, singleMaxDecimalDigits+1 ); - int iValue; - float fValue; - - // First, check for NaN and Infinity values - if(digits == infinity || digits == notANumber) { - if(digits == notANumber) - return Float.NaN; - else - return (isNegative?Float.NEGATIVE_INFINITY:Float.POSITIVE_INFINITY); - } - else { - /* - * convert the lead kDigits to an integer. - */ - iValue = (int)digits[0]-(int)'0'; - for ( int i=1; i < kDigits; i++ ){ - iValue = iValue*10 + (int)digits[i]-(int)'0'; - } - fValue = (float)iValue; - int exp = decExponent-kDigits; - /* - * iValue now contains an integer with the value of - * the first kDigits digits of the number. - * fValue contains the (float) of the same. - */ - - if ( nDigits <= singleMaxDecimalDigits ){ - /* - * possibly an easy case. - * We know that the digits can be represented - * exactly. And if the exponent isn't too outrageous, - * the whole thing can be done with one operation, - * thus one rounding error. - * Note that all our constructors trim all leading and - * trailing zeros, so simple values (including zero) - * will always end up here. - */ - if (exp == 0 || fValue == 0.0f) - return (isNegative)? -fValue : fValue; // small floating integer - else if ( exp >= 0 ){ - if ( exp <= singleMaxSmallTen ){ - /* - * Can get the answer with one operation, - * thus one roundoff. - */ - fValue *= singleSmall10pow[exp]; - return (isNegative)? -fValue : fValue; - } - int slop = singleMaxDecimalDigits - kDigits; - if ( exp <= singleMaxSmallTen+slop ){ - /* - * We can multiply dValue by 10^(slop) - * and it is still "small" and exact. - * Then we can multiply by 10^(exp-slop) - * with one rounding. - */ - fValue *= singleSmall10pow[slop]; - fValue *= singleSmall10pow[exp-slop]; - return (isNegative)? -fValue : fValue; - } - /* - * Else we have a hard case with a positive exp. - */ - } else { - if ( exp >= -singleMaxSmallTen ){ - /* - * Can get the answer in one division. - */ - fValue /= singleSmall10pow[-exp]; - return (isNegative)? -fValue : fValue; - } - /* - * Else we have a hard case with a negative exp. - */ - } - } else if ( (decExponent >= nDigits) && (nDigits+decExponent <= maxDecimalDigits) ){ - /* - * In double-precision, this is an exact floating integer. - * So we can compute to double, then shorten to float - * with one round, and get the right answer. - * - * First, finish accumulating digits. - * Then convert that integer to a double, multiply - * by the appropriate power of ten, and convert to float. - */ - long lValue = (long)iValue; - for ( int i=kDigits; i < nDigits; i++ ){ - lValue = lValue*10L + (long)((int)digits[i]-(int)'0'); - } - double dValue = (double)lValue; - exp = decExponent-nDigits; - dValue *= small10pow[exp]; - fValue = (float)dValue; - return (isNegative)? -fValue : fValue; - - } - /* - * Harder cases: - * The sum of digits plus exponent is greater than - * what we think we can do with one error. - * - * Start by weeding out obviously out-of-range - * results, then convert to double and go to - * common hard-case code. - */ - if ( decExponent > singleMaxDecimalExponent+1 ){ - /* - * Lets face it. This is going to be - * Infinity. Cut to the chase. - */ - return (isNegative)? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; - } else if ( decExponent < singleMinDecimalExponent-1 ){ - /* - * Lets face it. This is going to be - * zero. Cut to the chase. - */ - return (isNegative)? -0.0f : 0.0f; - } - - /* - * Here, we do 'way too much work, but throwing away - * our partial results, and going and doing the whole - * thing as double, then throwing away half the bits that computes - * when we convert back to float. - * - * The alternative is to reproduce the whole multiple-precision - * algorithm for float precision, or to try to parameterize it - * for common usage. The former will take about 400 lines of code, - * and the latter I tried without success. Thus the semi-hack - * answer here. - */ - mustSetRoundDir = !fromHex; - double dValue = doubleValue(); - return stickyRound( dValue ); - } - } - - - /* - * All the positive powers of 10 that can be - * represented exactly in double/float. - */ - private static final double small10pow[] = { - 1.0e0, - 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, - 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10, - 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, - 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20, - 1.0e21, 1.0e22 - }; - - private static final float singleSmall10pow[] = { - 1.0e0f, - 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f, - 1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f - }; - - private static final double big10pow[] = { - 1e16, 1e32, 1e64, 1e128, 1e256 }; - private static final double tiny10pow[] = { - 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; - - private static final int maxSmallTen = small10pow.length-1; - private static final int singleMaxSmallTen = singleSmall10pow.length-1; - - private static final int small5pow[] = { - 1, - 5, - 5*5, - 5*5*5, - 5*5*5*5, - 5*5*5*5*5, - 5*5*5*5*5*5, - 5*5*5*5*5*5*5, - 5*5*5*5*5*5*5*5, - 5*5*5*5*5*5*5*5*5, - 5*5*5*5*5*5*5*5*5*5, - 5*5*5*5*5*5*5*5*5*5*5, - 5*5*5*5*5*5*5*5*5*5*5*5, - 5*5*5*5*5*5*5*5*5*5*5*5*5 - }; - - - private static final long long5pow[] = { - 1L, - 5L, - 5L*5, - 5L*5*5, - 5L*5*5*5, - 5L*5*5*5*5, - 5L*5*5*5*5*5, - 5L*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5, - }; - - // approximately ceil( log2( long5pow[i] ) ) - private static final int n5bits[] = { - 0, - 3, - 5, - 7, - 10, - 12, - 14, - 17, - 19, - 21, - 24, - 26, - 28, - 31, - 33, - 35, - 38, - 40, - 42, - 45, - 47, - 49, - 52, - 54, - 56, - 59, - 61, - }; - - private static final char infinity[] = { 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y' }; - private static final char notANumber[] = { 'N', 'a', 'N' }; - private static final char zero[] = { '0', '0', '0', '0', '0', '0', '0', '0' }; - - - /* - * Grammar is compatible with hexadecimal floating-point constants - * described in section 6.4.4.2 of the C99 specification. - */ - private static Pattern hexFloatPattern = null; - private static synchronized Pattern getHexFloatPattern() { - if (hexFloatPattern == null) { - hexFloatPattern = Pattern.compile( - //1 234 56 7 8 9 - "([-+])?0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?" - ); - } - return hexFloatPattern; - } - - /* - * Convert string s to a suitable floating decimal; uses the - * double constructor and set the roundDir variable appropriately - * in case the value is later converted to a float. - */ - static OldFloatingDecimalForTest parseHexString(String s) { - // Verify string is a member of the hexadecimal floating-point - // string language. - Matcher m = getHexFloatPattern().matcher(s); - boolean validInput = m.matches(); - - if (!validInput) { - // Input does not match pattern - throw new NumberFormatException("For input string: \"" + s + "\""); - } else { // validInput - /* - * We must isolate the sign, significand, and exponent - * fields. The sign value is straightforward. Since - * floating-point numbers are stored with a normalized - * representation, the significand and exponent are - * interrelated. - * - * After extracting the sign, we normalized the - * significand as a hexadecimal value, calculating an - * exponent adjust for any shifts made during - * normalization. If the significand is zero, the - * exponent doesn't need to be examined since the output - * will be zero. - * - * Next the exponent in the input string is extracted. - * Afterwards, the significand is normalized as a *binary* - * value and the input value's normalized exponent can be - * computed. The significand bits are copied into a - * double significand; if the string has more logical bits - * than can fit in a double, the extra bits affect the - * round and sticky bits which are used to round the final - * value. - */ - - // Extract significand sign - String group1 = m.group(1); - double sign = (( group1 == null ) || group1.equals("+"))? 1.0 : -1.0; - - - // Extract Significand magnitude - /* - * Based on the form of the significand, calculate how the - * binary exponent needs to be adjusted to create a - * normalized *hexadecimal* floating-point number; that - * is, a number where there is one nonzero hex digit to - * the left of the (hexa)decimal point. Since we are - * adjusting a binary, not hexadecimal exponent, the - * exponent is adjusted by a multiple of 4. - * - * There are a number of significand scenarios to consider; - * letters are used in indicate nonzero digits: - * - * 1. 000xxxx => x.xxx normalized - * increase exponent by (number of x's - 1)*4 - * - * 2. 000xxx.yyyy => x.xxyyyy normalized - * increase exponent by (number of x's - 1)*4 - * - * 3. .000yyy => y.yy normalized - * decrease exponent by (number of zeros + 1)*4 - * - * 4. 000.00000yyy => y.yy normalized - * decrease exponent by (number of zeros to right of point + 1)*4 - * - * If the significand is exactly zero, return a properly - * signed zero. - */ - - String significandString =null; - int signifLength = 0; - int exponentAdjust = 0; - { - int leftDigits = 0; // number of meaningful digits to - // left of "decimal" point - // (leading zeros stripped) - int rightDigits = 0; // number of digits to right of - // "decimal" point; leading zeros - // must always be accounted for - /* - * The significand is made up of either - * - * 1. group 4 entirely (integer portion only) - * - * OR - * - * 2. the fractional portion from group 7 plus any - * (optional) integer portions from group 6. - */ - String group4; - if( (group4 = m.group(4)) != null) { // Integer-only significand - // Leading zeros never matter on the integer portion - significandString = stripLeadingZeros(group4); - leftDigits = significandString.length(); - } - else { - // Group 6 is the optional integer; leading zeros - // never matter on the integer portion - String group6 = stripLeadingZeros(m.group(6)); - leftDigits = group6.length(); - - // fraction - String group7 = m.group(7); - rightDigits = group7.length(); - - // Turn "integer.fraction" into "integer"+"fraction" - significandString = - ((group6 == null)?"":group6) + // is the null - // check necessary? - group7; - } - - significandString = stripLeadingZeros(significandString); - signifLength = significandString.length(); - - /* - * Adjust exponent as described above - */ - if (leftDigits >= 1) { // Cases 1 and 2 - exponentAdjust = 4*(leftDigits - 1); - } else { // Cases 3 and 4 - exponentAdjust = -4*( rightDigits - signifLength + 1); - } - - // If the significand is zero, the exponent doesn't - // matter; return a properly signed zero. - - if (signifLength == 0) { // Only zeros in input - return new OldFloatingDecimalForTest(sign * 0.0); - } - } - - // Extract Exponent - /* - * Use an int to read in the exponent value; this should - * provide more than sufficient range for non-contrived - * inputs. If reading the exponent in as an int does - * overflow, examine the sign of the exponent and - * significand to determine what to do. - */ - String group8 = m.group(8); - boolean positiveExponent = ( group8 == null ) || group8.equals("+"); - long unsignedRawExponent; - try { - unsignedRawExponent = Integer.parseInt(m.group(9)); - } - catch (NumberFormatException e) { - // At this point, we know the exponent is - // syntactically well-formed as a sequence of - // digits. Therefore, if an NumberFormatException - // is thrown, it must be due to overflowing int's - // range. Also, at this point, we have already - // checked for a zero significand. Thus the signs - // of the exponent and significand determine the - // final result: - // - // significand - // + - - // exponent + +infinity -infinity - // - +0.0 -0.0 - return new OldFloatingDecimalForTest(sign * (positiveExponent ? - Double.POSITIVE_INFINITY : 0.0)); - } - - long rawExponent = - (positiveExponent ? 1L : -1L) * // exponent sign - unsignedRawExponent; // exponent magnitude - - // Calculate partially adjusted exponent - long exponent = rawExponent + exponentAdjust ; - - // Starting copying non-zero bits into proper position in - // a long; copy explicit bit too; this will be masked - // later for normal values. - - boolean round = false; - boolean sticky = false; - int bitsCopied=0; - int nextShift=0; - long significand=0L; - // First iteration is different, since we only copy - // from the leading significand bit; one more exponent - // adjust will be needed... - - // IMPORTANT: make leadingDigit a long to avoid - // surprising shift semantics! - long leadingDigit = getHexDigit(significandString, 0); - - /* - * Left shift the leading digit (53 - (bit position of - * leading 1 in digit)); this sets the top bit of the - * significand to 1. The nextShift value is adjusted - * to take into account the number of bit positions of - * the leadingDigit actually used. Finally, the - * exponent is adjusted to normalize the significand - * as a binary value, not just a hex value. - */ - if (leadingDigit == 1) { - significand |= leadingDigit << 52; - nextShift = 52 - 4; - /* exponent += 0 */ } - else if (leadingDigit <= 3) { // [2, 3] - significand |= leadingDigit << 51; - nextShift = 52 - 5; - exponent += 1; - } - else if (leadingDigit <= 7) { // [4, 7] - significand |= leadingDigit << 50; - nextShift = 52 - 6; - exponent += 2; - } - else if (leadingDigit <= 15) { // [8, f] - significand |= leadingDigit << 49; - nextShift = 52 - 7; - exponent += 3; - } else { - throw new AssertionError("Result from digit conversion too large!"); - } - // The preceding if-else could be replaced by a single - // code block based on the high-order bit set in - // leadingDigit. Given leadingOnePosition, - - // significand |= leadingDigit << (SIGNIFICAND_WIDTH - leadingOnePosition); - // nextShift = 52 - (3 + leadingOnePosition); - // exponent += (leadingOnePosition-1); - - - /* - * Now the exponent variable is equal to the normalized - * binary exponent. Code below will make representation - * adjustments if the exponent is incremented after - * rounding (includes overflows to infinity) or if the - * result is subnormal. - */ - - // Copy digit into significand until the significand can't - // hold another full hex digit or there are no more input - // hex digits. - int i = 0; - for(i = 1; - i < signifLength && nextShift >= 0; - i++) { - long currentDigit = getHexDigit(significandString, i); - significand |= (currentDigit << nextShift); - nextShift-=4; - } - - // After the above loop, the bulk of the string is copied. - // Now, we must copy any partial hex digits into the - // significand AND compute the round bit and start computing - // sticky bit. - - if ( i < signifLength ) { // at least one hex input digit exists - long currentDigit = getHexDigit(significandString, i); - - // from nextShift, figure out how many bits need - // to be copied, if any - switch(nextShift) { // must be negative - case -1: - // three bits need to be copied in; can - // set round bit - significand |= ((currentDigit & 0xEL) >> 1); - round = (currentDigit & 0x1L) != 0L; - break; - - case -2: - // two bits need to be copied in; can - // set round and start sticky - significand |= ((currentDigit & 0xCL) >> 2); - round = (currentDigit &0x2L) != 0L; - sticky = (currentDigit & 0x1L) != 0; - break; - - case -3: - // one bit needs to be copied in - significand |= ((currentDigit & 0x8L)>>3); - // Now set round and start sticky, if possible - round = (currentDigit &0x4L) != 0L; - sticky = (currentDigit & 0x3L) != 0; - break; - - case -4: - // all bits copied into significand; set - // round and start sticky - round = ((currentDigit & 0x8L) != 0); // is top bit set? - // nonzeros in three low order bits? - sticky = (currentDigit & 0x7L) != 0; - break; - - default: - throw new AssertionError("Unexpected shift distance remainder."); - // break; - } - - // Round is set; sticky might be set. - - // For the sticky bit, it suffices to check the - // current digit and test for any nonzero digits in - // the remaining unprocessed input. - i++; - while(i < signifLength && !sticky) { - currentDigit = getHexDigit(significandString,i); - sticky = sticky || (currentDigit != 0); - i++; - } - - } - // else all of string was seen, round and sticky are - // correct as false. - - - // Check for overflow and update exponent accordingly. - - if (exponent > Double.MAX_EXPONENT) { // Infinite result - // overflow to properly signed infinity - return new OldFloatingDecimalForTest(sign * Double.POSITIVE_INFINITY); - } else { // Finite return value - if (exponent <= Double.MAX_EXPONENT && // (Usually) normal result - exponent >= Double.MIN_EXPONENT) { - - // The result returned in this block cannot be a - // zero or subnormal; however after the - // significand is adjusted from rounding, we could - // still overflow in infinity. - - // AND exponent bits into significand; if the - // significand is incremented and overflows from - // rounding, this combination will update the - // exponent correctly, even in the case of - // Double.MAX_VALUE overflowing to infinity. - - significand = (( (exponent + - (long)DoubleConsts.EXP_BIAS) << - (DoubleConsts.SIGNIFICAND_WIDTH-1)) - & DoubleConsts.EXP_BIT_MASK) | - (DoubleConsts.SIGNIF_BIT_MASK & significand); - - } else { // Subnormal or zero - // (exponent < Double.MIN_EXPONENT) - - if (exponent < (DoubleConsts.MIN_SUB_EXPONENT -1 )) { - // No way to round back to nonzero value - // regardless of significand if the exponent is - // less than -1075. - return new OldFloatingDecimalForTest(sign * 0.0); - } else { // -1075 <= exponent <= MIN_EXPONENT -1 = -1023 - /* - * Find bit position to round to; recompute - * round and sticky bits, and shift - * significand right appropriately. - */ - - sticky = sticky || round; - round = false; - - // Number of bits of significand to preserve is - // exponent - abs_min_exp +1 - // check: - // -1075 +1074 + 1 = 0 - // -1023 +1074 + 1 = 52 - - int bitsDiscarded = 53 - - ((int)exponent - DoubleConsts.MIN_SUB_EXPONENT + 1); - assert bitsDiscarded >= 1 && bitsDiscarded <= 53; - - // What to do here: - // First, isolate the new round bit - round = (significand & (1L << (bitsDiscarded -1))) != 0L; - if (bitsDiscarded > 1) { - // create mask to update sticky bits; low - // order bitsDiscarded bits should be 1 - long mask = ~((~0L) << (bitsDiscarded -1)); - sticky = sticky || ((significand & mask) != 0L ) ; - } - - // Now, discard the bits - significand = significand >> bitsDiscarded; - - significand = (( ((long)(Double.MIN_EXPONENT -1) + // subnorm exp. - (long)DoubleConsts.EXP_BIAS) << - (DoubleConsts.SIGNIFICAND_WIDTH-1)) - & DoubleConsts.EXP_BIT_MASK) | - (DoubleConsts.SIGNIF_BIT_MASK & significand); - } - } - - // The significand variable now contains the currently - // appropriate exponent bits too. - - /* - * Determine if significand should be incremented; - * making this determination depends on the least - * significant bit and the round and sticky bits. - * - * Round to nearest even rounding table, adapted from - * table 4.7 in "Computer Arithmetic" by IsraelKoren. - * The digit to the left of the "decimal" point is the - * least significant bit, the digits to the right of - * the point are the round and sticky bits - * - * Number Round(x) - * x0.00 x0. - * x0.01 x0. - * x0.10 x0. - * x0.11 x1. = x0. +1 - * x1.00 x1. - * x1.01 x1. - * x1.10 x1. + 1 - * x1.11 x1. + 1 - */ - boolean incremented = false; - boolean leastZero = ((significand & 1L) == 0L); - if( ( leastZero && round && sticky ) || - ((!leastZero) && round )) { - incremented = true; - significand++; - } - - OldFloatingDecimalForTest fd = new OldFloatingDecimalForTest(Math.copySign( - Double.longBitsToDouble(significand), - sign)); - - /* - * Set roundingDir variable field of fd properly so - * that the input string can be properly rounded to a - * float value. There are two cases to consider: - * - * 1. rounding to double discards sticky bit - * information that would change the result of a float - * rounding (near halfway case between two floats) - * - * 2. rounding to double rounds up when rounding up - * would not occur when rounding to float. - * - * For former case only needs to be considered when - * the bits rounded away when casting to float are all - * zero; otherwise, float round bit is properly set - * and sticky will already be true. - * - * The lower exponent bound for the code below is the - * minimum (normalized) subnormal exponent - 1 since a - * value with that exponent can round up to the - * minimum subnormal value and the sticky bit - * information must be preserved (i.e. case 1). - */ - if ((exponent >= FloatConsts.MIN_SUB_EXPONENT-1) && - (exponent <= Float.MAX_EXPONENT ) ){ - // Outside above exponent range, the float value - // will be zero or infinity. - - /* - * If the low-order 28 bits of a rounded double - * significand are 0, the double could be a - * half-way case for a rounding to float. If the - * double value is a half-way case, the double - * significand may have to be modified to round - * the the right float value (see the stickyRound - * method). If the rounding to double has lost - * what would be float sticky bit information, the - * double significand must be incremented. If the - * double value's significand was itself - * incremented, the float value may end up too - * large so the increment should be undone. - */ - if ((significand & 0xfffffffL) == 0x0L) { - // For negative values, the sign of the - // roundDir is the same as for positive values - // since adding 1 increasing the significand's - // magnitude and subtracting 1 decreases the - // significand's magnitude. If neither round - // nor sticky is true, the double value is - // exact and no adjustment is required for a - // proper float rounding. - if( round || sticky) { - if (leastZero) { // prerounding lsb is 0 - // If round and sticky were both true, - // and the least significant - // significand bit were 0, the rounded - // significand would not have its - // low-order bits be zero. Therefore, - // we only need to adjust the - // significand if round XOR sticky is - // true. - if (round ^ sticky) { - fd.roundDir = 1; - } - } - else { // prerounding lsb is 1 - // If the prerounding lsb is 1 and the - // resulting significand has its - // low-order bits zero, the significand - // was incremented. Here, we undo the - // increment, which will ensure the - // right guard and sticky bits for the - // float rounding. - if (round) - fd.roundDir = -1; - } - } - } - } - - fd.fromHex = true; - return fd; - } - } - } - - /** - * Return s with any leading zeros removed. - */ - static String stripLeadingZeros(String s) { - return s.replaceFirst("^0+", ""); - } - - /** - * Extract a hexadecimal digit from position position - * of string s. - */ - static int getHexDigit(String s, int position) { - int value = Character.digit(s.charAt(position), 16); - if (value <= -1 || value >= 16) { - throw new AssertionError("Unexpected failure of digit conversion of " + - s.charAt(position)); - } - return value; - } - - -} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/sun/misc/FloatingDecimal/TestFDBigInteger.java --- a/jdk/test/sun/misc/FloatingDecimal/TestFDBigInteger.java Wed Dec 23 15:41:50 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,435 +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.math.BigInteger; -import java.util.Random; -import sun.misc.FDBigInteger; - -/** - * @test - * @bug 7032154 - * @summary unit testys of sun.misc.FDBigInteger - * @modules java.base/sun.misc - * @author Dmitry Nadezhin - */ -public class TestFDBigInteger { - - private static final int MAX_P5 = 413; - private static final int MAX_P2 = 65; - private static final long LONG_SIGN_MASK = (1L << 63); - private static final BigInteger FIVE = BigInteger.valueOf(5); - private static final FDBigInteger MUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0)); - private static final FDBigInteger IMMUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0)); - private static final FDBigInteger IMMUTABLE_MILLION = genMillion1(); - private static final FDBigInteger IMMUTABLE_BILLION = genBillion1(); - private static final FDBigInteger IMMUTABLE_TEN18 = genTen18(); - - static { - IMMUTABLE_ZERO.makeImmutable(); - IMMUTABLE_MILLION.makeImmutable(); - IMMUTABLE_BILLION.makeImmutable(); - IMMUTABLE_TEN18.makeImmutable(); - } - - private static FDBigInteger mutable(String hex, int offset) { - char[] chars = new BigInteger(hex, 16).toString().toCharArray(); - return new FDBigInteger(0, chars, 0, chars.length).multByPow52(0, offset * 32); - } - - private static FDBigInteger immutable(String hex, int offset) { - FDBigInteger fd = mutable(hex, offset); - fd.makeImmutable(); - return fd; - } - - private static BigInteger biPow52(int p5, int p2) { - return FIVE.pow(p5).shiftLeft(p2); - } - - // data.length == 1, nWords == 1, offset == 0 - private static FDBigInteger genMillion1() { - return FDBigInteger.valueOfPow52(6, 0).leftShift(6); - } - - // data.length == 2, nWords == 1, offset == 0 - private static FDBigInteger genMillion2() { - return FDBigInteger.valueOfMulPow52(1000000L, 0, 0); - } - - // data.length == 1, nWords == 1, offset == 0 - private static FDBigInteger genBillion1() { - return FDBigInteger.valueOfPow52(9, 0).leftShift(9); - } - - // data.length == 2, nWords == 2, offset == 0 - private static FDBigInteger genTen18() { - return FDBigInteger.valueOfPow52(18, 0).leftShift(18); - } - - private static void check(BigInteger expected, FDBigInteger actual, String message) throws Exception { - if (!expected.equals(actual.toBigInteger())) { - throw new Exception(message + " result " + actual.toHexString() + " expected " + expected.toString(16)); - } - } - - private static void testValueOfPow52(int p5, int p2) throws Exception { - check(biPow52(p5, p2), FDBigInteger.valueOfPow52(p5, p2), - "valueOfPow52(" + p5 + "," + p2 + ")"); - } - - private static void testValueOfPow52() throws Exception { - for (int p5 = 0; p5 <= MAX_P5; p5++) { - for (int p2 = 0; p2 <= MAX_P2; p2++) { - testValueOfPow52(p5, p2); - } - } - } - - private static void testValueOfMulPow52(long value, int p5, int p2) throws Exception { - BigInteger bi = BigInteger.valueOf(value & ~LONG_SIGN_MASK); - if (value < 0) { - bi = bi.setBit(63); - } - check(biPow52(p5, p2).multiply(bi), FDBigInteger.valueOfMulPow52(value, p5, p2), - "valueOfMulPow52(" + Long.toHexString(value) + "." + p5 + "," + p2 + ")"); - } - - private static void testValueOfMulPow52(long value, int p5) throws Exception { - testValueOfMulPow52(value, p5, 0); - testValueOfMulPow52(value, p5, 1); - testValueOfMulPow52(value, p5, 30); - testValueOfMulPow52(value, p5, 31); - testValueOfMulPow52(value, p5, 33); - testValueOfMulPow52(value, p5, 63); - } - - private static void testValueOfMulPow52() throws Exception { - for (int p5 = 0; p5 <= MAX_P5; p5++) { - testValueOfMulPow52(0xFFFFFFFFL, p5); - testValueOfMulPow52(0x123456789AL, p5); - testValueOfMulPow52(0x7FFFFFFFFFFFFFFFL, p5); - testValueOfMulPow52(0xFFFFFFFFFFF54321L, p5); - } - } - - private static void testLeftShift(FDBigInteger t, int shift, boolean isImmutable) throws Exception { - BigInteger bt = t.toBigInteger(); - FDBigInteger r = t.leftShift(shift); - if ((bt.signum() == 0 || shift == 0 || !isImmutable) && r != t) { - throw new Exception("leftShift doesn't reuse its argument"); - } - if (isImmutable) { - check(bt, t, "leftShift corrupts its argument"); - } - check(bt.shiftLeft(shift), r, "leftShift returns wrong result"); - } - - private static void testLeftShift() throws Exception { - testLeftShift(IMMUTABLE_ZERO, 0, true); - testLeftShift(IMMUTABLE_ZERO, 10, true); - testLeftShift(MUTABLE_ZERO, 0, false); - testLeftShift(MUTABLE_ZERO, 10, false); - - testLeftShift(IMMUTABLE_MILLION, 0, true); - testLeftShift(IMMUTABLE_MILLION, 1, true); - testLeftShift(IMMUTABLE_MILLION, 12, true); - testLeftShift(IMMUTABLE_MILLION, 13, true); - testLeftShift(IMMUTABLE_MILLION, 32, true); - testLeftShift(IMMUTABLE_MILLION, 33, true); - testLeftShift(IMMUTABLE_MILLION, 44, true); - testLeftShift(IMMUTABLE_MILLION, 45, true); - - testLeftShift(genMillion1(), 0, false); - testLeftShift(genMillion1(), 1, false); - testLeftShift(genMillion1(), 12, false); - testLeftShift(genMillion1(), 13, false); - testLeftShift(genMillion1(), 25, false); - testLeftShift(genMillion1(), 26, false); - testLeftShift(genMillion1(), 32, false); - testLeftShift(genMillion1(), 33, false); - testLeftShift(genMillion1(), 44, false); - testLeftShift(genMillion1(), 45, false); - - testLeftShift(genMillion2(), 0, false); - testLeftShift(genMillion2(), 1, false); - testLeftShift(genMillion2(), 12, false); - testLeftShift(genMillion2(), 13, false); - testLeftShift(genMillion2(), 25, false); - testLeftShift(genMillion2(), 26, false); - testLeftShift(genMillion2(), 32, false); - testLeftShift(genMillion2(), 33, false); - testLeftShift(genMillion2(), 44, false); - testLeftShift(genMillion2(), 45, false); - } - - private static void testQuoRemIteration(FDBigInteger t, FDBigInteger s) throws Exception { - BigInteger bt = t.toBigInteger(); - BigInteger bs = s.toBigInteger(); - int q = t.quoRemIteration(s); - BigInteger[] qr = bt.divideAndRemainder(bs); - if (!BigInteger.valueOf(q).equals(qr[0])) { - throw new Exception("quoRemIteration returns incorrect quo"); - } - check(qr[1].multiply(BigInteger.TEN), t, "quoRemIteration returns incorrect rem"); - } - - private static void testQuoRemIteration() throws Exception { - // IMMUTABLE_TEN18 == 0de0b6b3a7640000 - // q = 0 - testQuoRemIteration(mutable("00000001", 0), IMMUTABLE_TEN18); - testQuoRemIteration(mutable("00000001", 1), IMMUTABLE_TEN18); - testQuoRemIteration(mutable("0de0b6b2", 1), IMMUTABLE_TEN18); - // q = 1 -> q = 0 - testQuoRemIteration(mutable("0de0b6b3", 1), IMMUTABLE_TEN18); - testQuoRemIteration(mutable("0de0b6b3a763FFFF", 0), IMMUTABLE_TEN18); - // q = 1 - testQuoRemIteration(mutable("0de0b6b3a7640000", 0), IMMUTABLE_TEN18); - testQuoRemIteration(mutable("0de0b6b3FFFFFFFF", 0), IMMUTABLE_TEN18); - testQuoRemIteration(mutable("8ac72304", 1), IMMUTABLE_TEN18); - testQuoRemIteration(mutable("0de0b6b400000000", 0), IMMUTABLE_TEN18); - testQuoRemIteration(mutable("8ac72305", 1), IMMUTABLE_TEN18); - // q = 18 - testQuoRemIteration(mutable("FFFFFFFF", 1), IMMUTABLE_TEN18); - } - - private static void testCmp(FDBigInteger t, FDBigInteger o) throws Exception { - BigInteger bt = t.toBigInteger(); - BigInteger bo = o.toBigInteger(); - int cmp = t.cmp(o); - int bcmp = bt.compareTo(bo); - if (bcmp != cmp) { - throw new Exception("cmp returns " + cmp + " expected " + bcmp); - } - check(bt, t, "cmp corrupts this"); - check(bo, o, "cmp corrupts other"); - if (o.cmp(t) != -cmp) { - throw new Exception("asymmetrical cmp"); - } - check(bt, t, "cmp corrupts this"); - check(bo, o, "cmp corrupts other"); - } - - private static void testCmp() throws Exception { - testCmp(mutable("FFFFFFFF", 0), mutable("100000000", 0)); - testCmp(mutable("FFFFFFFF", 0), mutable("1", 1)); - testCmp(mutable("5", 0), mutable("6", 0)); - testCmp(mutable("5", 0), mutable("5", 0)); - testCmp(mutable("5000000001", 0), mutable("500000001", 0)); - testCmp(mutable("5000000001", 0), mutable("6", 1)); - testCmp(mutable("5000000001", 0), mutable("5", 1)); - testCmp(mutable("5000000000", 0), mutable("5", 1)); - } - - private static void testCmpPow52(FDBigInteger t, int p5, int p2) throws Exception { - FDBigInteger o = FDBigInteger.valueOfPow52(p5, p2); - BigInteger bt = t.toBigInteger(); - BigInteger bo = biPow52(p5, p2); - int cmp = t.cmp(o); - int bcmp = bt.compareTo(bo); - if (bcmp != cmp) { - throw new Exception("cmpPow52 returns " + cmp + " expected " + bcmp); - } - check(bt, t, "cmpPow52 corrupts this"); - check(bo, o, "cmpPow5 corrupts other"); - } - - private static void testCmpPow52() throws Exception { - testCmpPow52(mutable("00000002", 1), 0, 31); - testCmpPow52(mutable("00000002", 1), 0, 32); - testCmpPow52(mutable("00000002", 1), 0, 33); - testCmpPow52(mutable("00000002", 1), 0, 34); - testCmpPow52(mutable("00000002", 1), 0, 64); - testCmpPow52(mutable("00000003", 1), 0, 32); - testCmpPow52(mutable("00000003", 1), 0, 33); - testCmpPow52(mutable("00000003", 1), 0, 34); - } - - private static void testAddAndCmp(FDBigInteger t, FDBigInteger x, FDBigInteger y) throws Exception { - BigInteger bt = t.toBigInteger(); - BigInteger bx = x.toBigInteger(); - BigInteger by = y.toBigInteger(); - int cmp = t.addAndCmp(x, y); - int bcmp = bt.compareTo(bx.add(by)); - if (bcmp != cmp) { - throw new Exception("addAndCmp returns " + cmp + " expected " + bcmp); - } - check(bt, t, "addAndCmp corrupts this"); - check(bx, x, "addAndCmp corrupts x"); - check(by, y, "addAndCmp corrupts y"); - } - - private static void testAddAndCmp() throws Exception { - testAddAndCmp(MUTABLE_ZERO, MUTABLE_ZERO, MUTABLE_ZERO); - testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, MUTABLE_ZERO); - testAddAndCmp(mutable("00000001", 0), mutable("00000001", 0), MUTABLE_ZERO); - testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000001", 0)); - testAddAndCmp(mutable("00000001", 0), mutable("00000002", 0), MUTABLE_ZERO); - testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000002", 0)); - testAddAndCmp(mutable("00000001", 2), mutable("FFFFFFFF", 0), mutable("FFFFFFFF", 0)); - testAddAndCmp(mutable("00000001", 0), mutable("00000001", 1), mutable("00000001", 0)); - - testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0F80000000", 1), mutable("F0F0F0F080000000", 1)); - testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0E80000000", 1), mutable("F0F0F0F080000000", 1)); - - testAddAndCmp(mutable("00000002", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1)); - testAddAndCmp(mutable("00000003", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1)); - testAddAndCmp(mutable("00000004", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1)); - testAddAndCmp(mutable("00000005", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1)); - - testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0)); - testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000001", 0)); - testAddAndCmp(mutable("00000002", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0)); - testAddAndCmp(mutable("00000003", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0)); - } - - private static void testMultBy10(FDBigInteger t, boolean isImmutable) throws Exception { - BigInteger bt = t.toBigInteger(); - FDBigInteger r = t.multBy10(); - if ((bt.signum() == 0 || !isImmutable) && r != t) { - throw new Exception("multBy10 of doesn't reuse its argument"); - } - if (isImmutable) { - check(bt, t, "multBy10 corrupts its argument"); - } - check(bt.multiply(BigInteger.TEN), r, "multBy10 returns wrong result"); - } - - private static void testMultBy10() throws Exception { - for (int p5 = 0; p5 <= MAX_P5; p5++) { - for (int p2 = 0; p2 <= MAX_P2; p2++) { - // This strange way of creating a value ensures that it is mutable. - FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2); - testMultBy10(value, false); - value.makeImmutable(); - testMultBy10(value, true); - } - } - } - - private static void testMultByPow52(FDBigInteger t, int p5, int p2) throws Exception { - BigInteger bt = t.toBigInteger(); - FDBigInteger r = t.multByPow52(p5, p2); - if (bt.signum() == 0 && r != t) { - throw new Exception("multByPow52 of doesn't reuse its argument"); - } - check(bt.multiply(biPow52(p5, p2)), r, "multByPow52 returns wrong result"); - } - - private static void testMultByPow52() throws Exception { - for (int p5 = 0; p5 <= MAX_P5; p5++) { - for (int p2 = 0; p2 <= MAX_P2; p2++) { - // This strange way of creating a value ensures that it is mutable. - FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2); - testMultByPow52(value, p5, p2); - } - } - } - - private static void testLeftInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception { - BigInteger biLeft = left.toBigInteger(); - BigInteger biRight = right.toBigInteger(); - FDBigInteger diff = left.leftInplaceSub(right); - if (!isImmutable && diff != left) { - throw new Exception("leftInplaceSub of doesn't reuse its argument"); - } - if (isImmutable) { - check(biLeft, left, "leftInplaceSub corrupts its left immutable argument"); - } - check(biRight, right, "leftInplaceSub corrupts its right argument"); - check(biLeft.subtract(biRight), diff, "leftInplaceSub returns wrong result"); - } - - private static void testLeftInplaceSub() throws Exception { - for (int p5 = 0; p5 <= MAX_P5; p5++) { - for (int p2 = 0; p2 <= MAX_P2; p2++) { -// for (int p5r = 0; p5r <= p5; p5r += 10) { -// for (int p2r = 0; p2r <= p2; p2r += 10) { - for (int p5r = 0; p5r <= p5; p5r++) { - for (int p2r = 0; p2r <= p2; p2r++) { - // This strange way of creating a value ensures that it is mutable. - FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2); - FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r); - testLeftInplaceSub(left, right, false); - left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2); - left.makeImmutable(); - testLeftInplaceSub(left, right, true); - } - } - } - } - } - - private static void testRightInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception { - BigInteger biLeft = left.toBigInteger(); - BigInteger biRight = right.toBigInteger(); - FDBigInteger diff = left.rightInplaceSub(right); - if (!isImmutable && diff != right) { - throw new Exception("rightInplaceSub of doesn't reuse its argument"); - } - check(biLeft, left, "leftInplaceSub corrupts its left argument"); - if (isImmutable) { - check(biRight, right, "leftInplaceSub corrupts its right immutable argument"); - } - try { - check(biLeft.subtract(biRight), diff, "rightInplaceSub returns wrong result"); - } catch (Exception e) { - System.out.println(biLeft+" - "+biRight+" = "+biLeft.subtract(biRight)); - throw e; - } - } - - private static void testRightInplaceSub() throws Exception { - for (int p5 = 0; p5 <= MAX_P5; p5++) { - for (int p2 = 0; p2 <= MAX_P2; p2++) { -// for (int p5r = 0; p5r <= p5; p5r += 10) { -// for (int p2r = 0; p2r <= p2; p2r += 10) { - for (int p5r = 0; p5r <= p5; p5r++) { - for (int p2r = 0; p2r <= p2; p2r++) { - // This strange way of creating a value ensures that it is mutable. - FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2); - FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r); - testRightInplaceSub(left, right, false); - right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r); - right.makeImmutable(); - testRightInplaceSub(left, right, true); - } - } - } - } - } - - public static void main(String[] args) throws Exception { - testValueOfPow52(); - testValueOfMulPow52(); - testLeftShift(); - testQuoRemIteration(); - testCmp(); - testCmpPow52(); - testAddAndCmp(); - // Uncomment the following for more comprehensize but slow testing. - // testLeftInplaceSub(); - // testMultBy10(); - // testMultByPow52(); - // testRightInplaceSub(); - } -} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/sun/misc/FloatingDecimal/TestFloatingDecimal.java --- a/jdk/test/sun/misc/FloatingDecimal/TestFloatingDecimal.java Wed Dec 23 15:41:50 2015 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,329 +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.Random; -import sun.misc.FloatingDecimal; - -/* -OldFloatingDecimalForTest - -public class OldFloatingDecimalForTest { - public boolean digitsRoundedUp(); - public OldFloatingDecimalForTest(double); - public OldFloatingDecimalForTest(float); - public boolean decimalDigitsExact(); - public java.lang.String toString(); - public java.lang.String toJavaFormatString(); - public void appendTo(java.lang.Appendable); - public static OldFloatingDecimalForTest readJavaFormatString(java.lang.String) throws java.lang.NumberFormatException; - public strictfp double doubleValue(); - public strictfp float floatValue(); -} - -sun.misc.FloatingDecimal - -public class sun.misc.FloatingDecimal { - public sun.misc.FloatingDecimal(); - public static java.lang.String toJavaFormatString(double); - public static java.lang.String toJavaFormatString(float); - public static void appendTo(double, java.lang.Appendable); - public static void appendTo(float, java.lang.Appendable); - public static double parseDouble(java.lang.String) throws java.lang.NumberFormatException; - public static float parseFloat(java.lang.String) throws java.lang.NumberFormatException; - public static sun.misc.FloatingDecimal$AbstractD2ABuffer getD2ABuffer(double); -} -*/ - -/** - * @test - * @bug 7032154 - * @summary unit tests of sun.misc.FloatingDecimal - * @modules java.base/sun.misc - * @library ../../../java/lang/Math - * @build DoubleConsts FloatConsts - * @run main TestFloatingDecimal - * @author Brian Burkhalter - * @key randomness - */ -public class TestFloatingDecimal { - private static enum ResultType { - RESULT_EXCEPTION, - RESULT_PRINT - } - - private static final ResultType RESULT_TYPE = ResultType.RESULT_PRINT; - private static final int NUM_RANDOM_TESTS = 100000; - - private static final Random RANDOM = new Random(); - - private static void result(String message) { - switch (RESULT_TYPE) { - case RESULT_EXCEPTION: - throw new RuntimeException(message); - case RESULT_PRINT: - System.err.println(message); - break; - default: - assert false; - } - } - - private static int check(String test, Object expected, Object actual) { - int failures = 0; - if(!actual.equals(expected)) { - failures++; - result("Test "+test+" expected "+expected+" but obtained "+actual); - } - return failures; - } - - private static int testAppendToDouble() { - System.out.println(" testAppendToDouble"); - int failures = 0; - - for(int i = 0; i < NUM_RANDOM_TESTS; i++) { - double[] d = new double[] { - RANDOM.nextLong(), - RANDOM.nextGaussian(), - RANDOM.nextDouble()*Double.MAX_VALUE - }; - for(int j = 0; j < d.length; j++) { - OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]); - StringBuilder sb = new StringBuilder(); - ofd.appendTo(sb); - String oldString = sb.toString(); - sb = new StringBuilder(); - FloatingDecimal.appendTo(d[j], sb); - String newString = sb.toString(); - failures += check("testAppendToDouble", oldString, newString); - } - } - - return failures; - } - - private static int testAppendToFloat() { - System.out.println(" testAppendToFloat"); - int failures = 0; - - for(int i = 0; i < NUM_RANDOM_TESTS; i++) { - float[] f = new float[] { - RANDOM.nextLong(), - (float)RANDOM.nextGaussian(), - RANDOM.nextFloat()*Float.MAX_VALUE - }; - for(int j = 0; j < f.length; j++) { - OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]); - StringBuilder sb = new StringBuilder(); - ofd.appendTo(sb); - String oldString = sb.toString(); - sb = new StringBuilder(); - FloatingDecimal.appendTo(f[j], sb); - String newString = sb.toString(); - failures += check("testAppendToFloat", oldString, newString); - } - } - - return failures; - } - - private static int testAppendTo() { - System.out.println("testAppendTo"); - int failures = 0; - - failures += testAppendToDouble(); - failures += testAppendToFloat(); - - return failures; - } - - private static int testParseDouble() { - System.out.println(" testParseDouble"); - int failures = 0; - - for(int i = 0; i < NUM_RANDOM_TESTS; i++) { - double[] d = new double[] { - RANDOM.nextLong(), - RANDOM.nextGaussian(), - RANDOM.nextDouble()*Double.MAX_VALUE - }; - for(int j = 0; j < d.length; j++) { - OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]); - String javaFormatString = ofd.toJavaFormatString(); - ofd = OldFloatingDecimalForTest.readJavaFormatString(javaFormatString); - double oldDouble = ofd.doubleValue(); - double newDouble = FloatingDecimal.parseDouble(javaFormatString); - failures += check("testParseDouble", oldDouble, newDouble); - } - } - - return failures; - } - - private static int testParseFloat() { - System.out.println(" testParseFloat"); - int failures = 0; - - for(int i = 0; i < NUM_RANDOM_TESTS; i++) { - float[] f = new float[] { - RANDOM.nextInt(), - (float)RANDOM.nextGaussian(), - RANDOM.nextFloat()*Float.MAX_VALUE - }; - for(int j = 0; j < f.length; j++) { - OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]); - String javaFormatString = ofd.toJavaFormatString(); - ofd = OldFloatingDecimalForTest.readJavaFormatString(javaFormatString); - float oldFloat = ofd.floatValue(); - float newFloat = FloatingDecimal.parseFloat(javaFormatString); - failures += check("testParseFloat", oldFloat, newFloat); - } - } - - return failures; - } - - private static int testParse() { - System.out.println("testParse"); - int failures = 0; - - failures += testParseDouble(); - failures += testParseFloat(); - - return failures; - } - - private static int testToJavaFormatStringDoubleFixed() { - System.out.println(" testToJavaFormatStringDoubleFixed"); - int failures = 0; - - double[] d = new double [] { - -5.9522650387500933e18, // dtoa() fast path - 0.872989018674569, // dtoa() fast iterative - long - 1.1317400099603851e308 // dtoa() slow iterative - }; - - for(int i = 0; i < d.length; i++) { - OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[i]); - failures += check("testToJavaFormatStringDoubleFixed", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(d[i])); - } - - return failures; - } - - private static int testToJavaFormatStringDoubleRandom() { - System.out.println(" testToJavaFormatStringDoubleRandom"); - int failures = 0; - - for(int i = 0; i < NUM_RANDOM_TESTS; i++) { - double[] d = new double[] { - RANDOM.nextLong(), - RANDOM.nextGaussian(), - RANDOM.nextDouble()*Double.MAX_VALUE - }; - for(int j = 0; j < d.length; j++) { - OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]); - failures += check("testToJavaFormatStringDoubleRandom", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(d[j])); - } - } - - return failures; - } - - private static int testToJavaFormatStringDouble() { - System.out.println(" testToJavaFormatStringDouble"); - int failures = 0; - failures += testToJavaFormatStringDoubleFixed(); - failures += testToJavaFormatStringDoubleRandom(); - return failures; - } - - private static int testToJavaFormatStringFloatFixed() { - System.out.println(" testToJavaFormatStringFloatFixed"); - int failures = 0; - - float[] f = new float[] { - -9.8784166e8f, // dtoa() fast path - 0.70443946f, // dtoa() fast iterative - int - 1.8254228e37f // dtoa() slow iterative - }; - - for(int i = 0; i < f.length; i++) { - OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[i]); - failures += check("testToJavaFormatStringFloatFixed", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(f[i])); - } - - return failures; - } - - private static int testToJavaFormatStringFloatRandom() { - System.out.println(" testToJavaFormatStringFloatRandom"); - int failures = 0; - - for(int i = 0; i < NUM_RANDOM_TESTS; i++) { - float[] f = new float[] { - RANDOM.nextInt(), - (float)RANDOM.nextGaussian(), - RANDOM.nextFloat()*Float.MAX_VALUE - }; - for(int j = 0; j < f.length; j++) { - OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]); - failures += check("testToJavaFormatStringFloatRandom", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(f[j])); - } - } - - return failures; - } - - private static int testToJavaFormatStringFloat() { - System.out.println(" testToJavaFormatStringFloat"); - int failures = 0; - - failures += testToJavaFormatStringFloatFixed(); - failures += testToJavaFormatStringFloatRandom(); - - return failures; - } - - private static int testToJavaFormatString() { - System.out.println("testToJavaFormatString"); - int failures = 0; - - failures += testToJavaFormatStringDouble(); - failures += testToJavaFormatStringFloat(); - - return failures; - } - - public static void main(String[] args) { - int failures = 0; - - failures += testAppendTo(); - failures += testParse(); - failures += testToJavaFormatString(); - - if (failures != 0) { - throw new RuntimeException("" + failures + " failures while testing FloatingDecimal"); - } - } -} diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh --- a/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh Wed Jul 05 21:10:34 2017 +0200 @@ -69,8 +69,8 @@ # First part: output format # ========================================================== -$KT -genkeypair -alias a1 -dname CN=a1 -validity 365 -$KT -genkeypair -alias a2 -dname CN=a2 -validity 365 +$KT -genkeypair -alias a1 -dname CN=a1 -validity 366 +$KT -genkeypair -alias a2 -dname CN=a2 -validity 366 # a.jar includes 8 unsigned, 2 signed by a1 and a2, 2 signed by a3 $JAR cvf a.jar A1.class A2.class diff -r 9f30c43b7024 -r 29595f2d11d8 jdk/test/sun/tools/jstatd/TestJstatdServer.java --- a/jdk/test/sun/tools/jstatd/TestJstatdServer.java Wed Dec 23 15:41:50 2015 -0800 +++ b/jdk/test/sun/tools/jstatd/TestJstatdServer.java Wed Jul 05 21:10:34 2017 +0200 @@ -24,6 +24,7 @@ /* * @test * @bug 4990825 + * @key intermittent * @library /lib/testlibrary * @modules java.management * @build jdk.testlibrary.* JstatdTest JstatGCUtilParser diff -r 9f30c43b7024 -r 29595f2d11d8 modules.xml --- a/modules.xml Wed Dec 23 15:41:50 2015 -0800 +++ b/modules.xml Wed Jul 05 21:10:34 2017 +0200 @@ -223,6 +223,10 @@ jdk.dev + jdk.internal.math + java.desktop + + jdk.internal.misc java.corba java.desktop @@ -295,6 +299,7 @@ jdk.security.jgss jdk.snmp jdk.vm.ci + jdk.zipfs java.instrument