Merge
authorjwilhelm
Thu, 15 Feb 2018 16:16:17 +0100
changeset 48977 083e6c55a28c
parent 48900 276b0604eab3 (diff)
parent 48976 324105aaeddf (current diff)
child 48978 93996c47d36f
Merge
--- a/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java	Thu Feb 15 09:17:56 2018 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java	Thu Feb 15 16:16:17 2018 +0100
@@ -67,12 +67,12 @@
             // VM is pushing arguments at us
             pullModeBSM = null;
             if (pullMode) {
-                bootstrapMethod = Adapters.pushMePullYou(bootstrapMethod, true);
+                bootstrapMethod = pushMePullYou(bootstrapMethod, true);
             }
         } else {
             // VM wants us to pull args from it
             pullModeBSM = pullMode ? bootstrapMethod :
-                    Adapters.pushMePullYou(bootstrapMethod, false);
+                    pushMePullYou(bootstrapMethod, false);
             bootstrapMethod = null;
         }
         try {
@@ -237,9 +237,10 @@
             // give up at first null and grab the rest in one big block
             if (i >= end)  return i;
             Object[] temp = new Object[end - i];
-            if (TRACE_METHOD_LINKAGE)
-                System.out.println("resolving more BSM arguments: "+
+            if (TRACE_METHOD_LINKAGE) {
+                System.out.println("resolving more BSM arguments: " +
                         Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end));
+            }
             copyOutBootstrapArguments(caller, indexInfo,
                                       i, end, temp, 0,
                                       true, null);
@@ -285,9 +286,10 @@
         private void prefetchIntoCache(int i, int pfLimit) {
             if (pfLimit <= i)  return;  // corner case
             Object[] temp = new Object[pfLimit - i];
-            if (TRACE_METHOD_LINKAGE)
-                System.out.println("prefetching BSM arguments: "+
+            if (TRACE_METHOD_LINKAGE) {
+                System.out.println("prefetching BSM arguments: " +
                         Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit));
+            }
             copyOutBootstrapArguments(caller, indexInfo,
                                       i, pfLimit, temp, 0,
                                       false, NOT_PRESENT);
@@ -301,7 +303,7 @@
     }
 
     /*non-public*/ static final
-    class Adapters {
+    class PushAdapter {
         // skeleton for push-mode BSM which wraps a pull-mode BSM:
         static Object pushToBootstrapMethod(MethodHandle pullModeBSM,
                                             MethodHandles.Lookup lookup, String name, Object type,
@@ -313,29 +315,75 @@
             return pullModeBSM.invoke(lookup, bsci);
         }
 
-        // skeleton for pull-mode BSM which wraps a push-mode BSM:
-        static Object pullFromBootstrapMethod(MethodHandle pushModeBSM,
-                                              MethodHandles.Lookup lookup, BootstrapCallInfo<?> bsci)
-                throws Throwable {
-            int argc = bsci.size();
-            Object arguments[] = new Object[3 + argc];
-            arguments[0] = lookup;
-            arguments[1] = bsci.invocationName();
-            arguments[2] = bsci.invocationType();
-            bsci.copyConstants(0, argc, arguments, 3);
-            if (TRACE_METHOD_LINKAGE)
-                System.out.println("pulled arguments from VM for push-mode BSM");
-            return pushModeBSM.invokeWithArguments(arguments);
-        }
         static final MethodHandle MH_pushToBootstrapMethod;
-        static final MethodHandle MH_pullFromBootstrapMethod;
         static {
-            final Class<?> THIS_CLASS = Adapters.class;
+            final Class<?> THIS_CLASS = PushAdapter.class;
             try {
                 MH_pushToBootstrapMethod = IMPL_LOOKUP
                     .findStatic(THIS_CLASS, "pushToBootstrapMethod",
                                 MethodType.methodType(Object.class, MethodHandle.class,
                                         Lookup.class, String.class, Object.class, Object[].class));
+            } catch (Throwable ex) {
+                throw new InternalError(ex);
+            }
+        }
+    }
+
+    /*non-public*/ static final
+    class PullAdapter {
+        // skeleton for pull-mode BSM which wraps a push-mode BSM:
+        static Object pullFromBootstrapMethod(MethodHandle pushModeBSM,
+                                              MethodHandles.Lookup lookup,
+                                              BootstrapCallInfo<?> bsci)
+                throws Throwable {
+            int argc = bsci.size();
+            switch (argc) {
+                case 0:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType());
+                case 1:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0));
+                case 2:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0), bsci.get(1));
+                case 3:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0), bsci.get(1), bsci.get(2));
+                case 4:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3));
+                case 5:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4));
+                case 6:
+                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
+                            bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5));
+                default:
+                    final int NON_SPREAD_ARG_COUNT = 3;  // (lookup, name, type)
+                    final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
+                    if (argc >= MAX_SAFE_SIZE) {
+                        // to be on the safe side, use invokeWithArguments which handles jumbo lists
+                        Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argc];
+                        newargv[0] = lookup;
+                        newargv[1] = bsci.invocationName();
+                        newargv[2] = bsci.invocationType();
+                        bsci.copyConstants(0, argc, newargv, NON_SPREAD_ARG_COUNT);
+                        return pushModeBSM.invokeWithArguments(newargv);
+                    }
+                    MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argc);
+                    MethodHandle typedBSM = pushModeBSM.asType(invocationType);
+                    MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
+                    Object[] argv = new Object[argc];
+                    bsci.copyConstants(0, argc, argv, 0);
+                    return spreader.invokeExact(typedBSM, (Object) lookup, (Object) bsci.invocationName(), bsci.invocationType(), argv);
+                }
+        }
+
+        static final MethodHandle MH_pullFromBootstrapMethod;
+
+        static {
+            final Class<?> THIS_CLASS = PullAdapter.class;
+            try {
                 MH_pullFromBootstrapMethod = IMPL_LOOKUP
                     .findStatic(THIS_CLASS, "pullFromBootstrapMethod",
                                 MethodType.methodType(Object.class, MethodHandle.class,
@@ -344,23 +392,25 @@
                 throw new InternalError(ex);
             }
         }
+    }
 
-        /** Given a push-mode BSM (taking one argument) convert it to a
-         *  pull-mode BSM (taking N pre-resolved arguments).
-         *  This method is used when, in fact, the JVM is passing up
-         *  pre-resolved arguments, but the BSM is expecting lazy stuff.
-         *  Or, when goToPushMode is true, do the reverse transform.
-         *  (The two transforms are exactly inverse.)
-         */
-        static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) {
-            if (TRACE_METHOD_LINKAGE)
-                System.out.println("converting BSM to "+(goToPushMode ? "push mode" : "pull mode"));
-            assert(isPullModeBSM(bsm) == goToPushMode);  //there must be a change
-            if (goToPushMode) {
-                return Adapters.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
-            } else {
-                return Adapters.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
-            }
+    /** Given a push-mode BSM (taking one argument) convert it to a
+     *  pull-mode BSM (taking N pre-resolved arguments).
+     *  This method is used when, in fact, the JVM is passing up
+     *  pre-resolved arguments, but the BSM is expecting lazy stuff.
+     *  Or, when goToPushMode is true, do the reverse transform.
+     *  (The two transforms are exactly inverse.)
+     */
+    static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) {
+        if (TRACE_METHOD_LINKAGE) {
+            System.out.println("converting BSM of type " + bsm.type() + " to "
+                    + (goToPushMode ? "push mode" : "pull mode"));
+        }
+        assert(isPullModeBSM(bsm) == goToPushMode); // there must be a change
+        if (goToPushMode) {
+            return PushAdapter.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
+        } else {
+            return PullAdapter.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
         }
     }
 }
--- a/src/java.base/share/classes/java/util/Base64.java	Thu Feb 15 09:17:56 2018 +0100
+++ b/src/java.base/share/classes/java/util/Base64.java	Thu Feb 15 16:16:17 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -692,7 +692,27 @@
             int dp = 0;
             int bits = 0;
             int shiftto = 18;       // pos of first byte of 4-byte atom
+
             while (sp < sl) {
+                if (shiftto == 18 && sp + 4 < sl) {       // fast path
+                    int sl0 = sp + ((sl - sp) & ~0b11);
+                    while (sp < sl0) {
+                        int b1 = base64[src[sp++] & 0xff];
+                        int b2 = base64[src[sp++] & 0xff];
+                        int b3 = base64[src[sp++] & 0xff];
+                        int b4 = base64[src[sp++] & 0xff];
+                        if ((b1 | b2 | b3 | b4) < 0) {    // non base64 byte
+                            sp -= 4;
+                            break;
+                        }
+                        int bits0 = b1 << 18 | b2 << 12 | b3 << 6 | b4;
+                        dst[dp++] = (byte)(bits0 >> 16);
+                        dst[dp++] = (byte)(bits0 >>  8);
+                        dst[dp++] = (byte)(bits0);
+                    }
+                    if (sp >= sl)
+                        break;
+                }
                 int b = src[sp++] & 0xff;
                 if ((b = base64[b]) < 0) {
                     if (b == -2) {         // padding byte '='
@@ -762,6 +782,7 @@
         private final int linemax;
         private final boolean doPadding;// whether or not to pad
         private int linepos = 0;
+        private byte[] buf;
 
         EncOutputStream(OutputStream os, char[] base64,
                         byte[] newline, int linemax, boolean doPadding) {
@@ -770,6 +791,7 @@
             this.newline = newline;
             this.linemax = linemax;
             this.doPadding = doPadding;
+            this.buf = new byte[linemax <= 0 ? 8124 : linemax];
         }
 
         @Override
@@ -786,6 +808,14 @@
             }
         }
 
+        private void writeb4(char b1, char b2, char b3, char b4) throws IOException {
+            buf[0] = (byte)b1;
+            buf[1] = (byte)b2;
+            buf[2] = (byte)b3;
+            buf[3] = (byte)b4;
+            out.write(buf, 0, 4);
+        }
+
         @Override
         public void write(byte[] b, int off, int len) throws IOException {
             if (closed)
@@ -806,25 +836,34 @@
                 b2 = b[off++] & 0xff;
                 len--;
                 checkNewline();
-                out.write(base64[b0 >> 2]);
-                out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
-                out.write(base64[(b1 << 2) & 0x3f | (b2 >> 6)]);
-                out.write(base64[b2 & 0x3f]);
+                writeb4(base64[b0 >> 2],
+                        base64[(b0 << 4) & 0x3f | (b1 >> 4)],
+                        base64[(b1 << 2) & 0x3f | (b2 >> 6)],
+                        base64[b2 & 0x3f]);
                 linepos += 4;
             }
             int nBits24 = len / 3;
             leftover = len - (nBits24 * 3);
-            while (nBits24-- > 0) {
+
+            while (nBits24 > 0) {
                 checkNewline();
-                int bits = (b[off++] & 0xff) << 16 |
-                           (b[off++] & 0xff) <<  8 |
-                           (b[off++] & 0xff);
-                out.write(base64[(bits >>> 18) & 0x3f]);
-                out.write(base64[(bits >>> 12) & 0x3f]);
-                out.write(base64[(bits >>> 6)  & 0x3f]);
-                out.write(base64[bits & 0x3f]);
-                linepos += 4;
-           }
+                int dl = linemax <= 0 ? buf.length : buf.length - linepos;
+                int sl = off + Math.min(nBits24, dl / 4) * 3;
+                int dp = 0;
+                for (int sp = off; sp < sl; ) {
+                    int bits = (b[sp++] & 0xff) << 16 |
+                               (b[sp++] & 0xff) <<  8 |
+                               (b[sp++] & 0xff);
+                    buf[dp++] = (byte)base64[(bits >>> 18) & 0x3f];
+                    buf[dp++] = (byte)base64[(bits >>> 12) & 0x3f];
+                    buf[dp++] = (byte)base64[(bits >>> 6)  & 0x3f];
+                    buf[dp++] = (byte)base64[bits & 0x3f];
+                }
+                out.write(buf, 0, dp);
+                off = sl;
+                linepos += dp;
+                nBits24 -= dp / 4;
+            }
             if (leftover == 1) {
                 b0 = b[off++] & 0xff;
             } else if (leftover == 2) {
@@ -889,6 +928,52 @@
             return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff;
         }
 
+        private int eof(byte[] b, int off, int len, int oldOff)
+            throws IOException
+        {
+            eof = true;
+            if (nextin != 18) {
+                if (nextin == 12)
+                    throw new IOException("Base64 stream has one un-decoded dangling byte.");
+                // treat ending xx/xxx without padding character legal.
+                // same logic as v == '=' below
+                b[off++] = (byte)(bits >> (16));
+                if (nextin == 0) {           // only one padding byte
+                    if (len == 1) {          // no enough output space
+                        bits >>= 8;          // shift to lowest byte
+                        nextout = 0;
+                    } else {
+                        b[off++] = (byte) (bits >>  8);
+                    }
+                }
+            }
+            return off == oldOff ? -1 : off - oldOff;
+        }
+
+        private int padding(byte[] b, int off, int len, int oldOff)
+            throws IOException
+        {
+            // =     shiftto==18 unnecessary padding
+            // x=    shiftto==12 dangling x, invalid unit
+            // xx=   shiftto==6 && missing last '='
+            // xx=y  or last is not '='
+            if (nextin == 18 || nextin == 12 ||
+                nextin == 6 && is.read() != '=') {
+                throw new IOException("Illegal base64 ending sequence:" + nextin);
+            }
+            b[off++] = (byte)(bits >> (16));
+            if (nextin == 0) {           // only one padding byte
+                if (len == 1) {          // no enough output space
+                    bits >>= 8;          // shift to lowest byte
+                    nextout = 0;
+                } else {
+                    b[off++] = (byte) (bits >>  8);
+                }
+            }
+            eof = true;
+            return off - oldOff;
+        }
+
         @Override
         public int read(byte[] b, int off, int len) throws IOException {
             if (closed)
@@ -898,82 +983,46 @@
             if (off < 0 || len < 0 || len > b.length - off)
                 throw new IndexOutOfBoundsException();
             int oldOff = off;
-            if (nextout >= 0) {       // leftover output byte(s) in bits buf
-                do {
-                    if (len == 0)
-                        return off - oldOff;
-                    b[off++] = (byte)(bits >> nextout);
-                    len--;
-                    nextout -= 8;
-                } while (nextout >= 0);
-                bits = 0;
+            while (nextout >= 0) {       // leftover output byte(s) in bits buf
+                if (len == 0)
+                    return off - oldOff;
+                b[off++] = (byte)(bits >> nextout);
+                len--;
+                nextout -= 8;
             }
+            bits = 0;
             while (len > 0) {
                 int v = is.read();
                 if (v == -1) {
-                    eof = true;
-                    if (nextin != 18) {
-                        if (nextin == 12)
-                            throw new IOException("Base64 stream has one un-decoded dangling byte.");
-                        // treat ending xx/xxx without padding character legal.
-                        // same logic as v == '=' below
-                        b[off++] = (byte)(bits >> (16));
-                        len--;
-                        if (nextin == 0) {           // only one padding byte
-                            if (len == 0) {          // no enough output space
-                                bits >>= 8;          // shift to lowest byte
-                                nextout = 0;
-                            } else {
-                                b[off++] = (byte) (bits >>  8);
-                            }
-                        }
-                    }
-                    if (off == oldOff)
-                        return -1;
-                    else
-                        return off - oldOff;
+                    return eof(b, off, len, oldOff);
                 }
-                if (v == '=') {                  // padding byte(s)
-                    // =     shiftto==18 unnecessary padding
-                    // x=    shiftto==12 dangling x, invalid unit
-                    // xx=   shiftto==6 && missing last '='
-                    // xx=y  or last is not '='
-                    if (nextin == 18 || nextin == 12 ||
-                        nextin == 6 && is.read() != '=') {
-                        throw new IOException("Illegal base64 ending sequence:" + nextin);
+                if ((v = base64[v]) < 0) {
+                    if (v == -2) {       // padding byte(s)
+                        return padding(b, off, len, oldOff);
                     }
-                    b[off++] = (byte)(bits >> (16));
-                    len--;
-                    if (nextin == 0) {           // only one padding byte
-                        if (len == 0) {          // no enough output space
-                            bits >>= 8;          // shift to lowest byte
-                            nextout = 0;
-                        } else {
-                            b[off++] = (byte) (bits >>  8);
-                        }
+                    if (v == -1) {
+                        if (!isMIME)
+                            throw new IOException("Illegal base64 character " +
+                                Integer.toString(v, 16));
+                        continue;        // skip if for rfc2045
                     }
-                    eof = true;
-                    break;
-                }
-                if ((v = base64[v]) == -1) {
-                    if (isMIME)                 // skip if for rfc2045
-                        continue;
-                    else
-                        throw new IOException("Illegal base64 character " +
-                            Integer.toString(v, 16));
+                    // neve be here
                 }
                 bits |= (v << nextin);
                 if (nextin == 0) {
-                    nextin = 18;    // clear for next
-                    nextout = 16;
-                    while (nextout >= 0) {
-                        b[off++] = (byte)(bits >> nextout);
-                        len--;
-                        nextout -= 8;
-                        if (len == 0 && nextout >= 0) {  // don't clean "bits"
-                            return off - oldOff;
-                        }
+                    nextin = 18;         // clear for next in
+                    b[off++] = (byte)(bits >> 16);
+                    if (len == 1) {
+                        nextout = 8;    // 2 bytes left in bits
+                        break;
                     }
+                    b[off++] = (byte)(bits >> 8);
+                    if (len == 2) {
+                        nextout = 0;    // 1 byte left in bits
+                        break;
+                    }
+                    b[off++] = (byte)bits;
+                    len -= 3;
                     bits = 0;
                 } else {
                     nextin -= 6;
--- a/src/java.base/share/classes/java/util/Collections.java	Thu Feb 15 09:17:56 2018 +0100
+++ b/src/java.base/share/classes/java/util/Collections.java	Thu Feb 15 16:16:17 2018 +0100
@@ -3771,9 +3771,9 @@
                  * Ensure that we don't get an ArrayStoreException even if
                  * s.toArray returns an array of something other than Object
                  */
-                Object[] dest = (CheckedEntry.class.isInstance(
-                    source.getClass().getComponentType()) ? source :
-                                 new Object[source.length]);
+                Object[] dest = (source.getClass() == Object[].class)
+                    ? source
+                    : new Object[source.length];
 
                 for (int i = 0; i < source.length; i++)
                     dest[i] = checkedEntry((Map.Entry<K,V>)source[i],
--- a/src/java.base/share/classes/sun/net/www/ParseUtil.java	Thu Feb 15 09:17:56 2018 +0100
+++ b/src/java.base/share/classes/sun/net/www/ParseUtil.java	Thu Feb 15 16:16:17 2018 +0100
@@ -44,7 +44,9 @@
  * @author  Mike McCloskey
  */
 
-public class ParseUtil {
+public final class ParseUtil {
+
+    private ParseUtil() {}
 
     /**
      * Constructs an encoded version of the specified path string suitable
@@ -80,10 +82,13 @@
         int len = path.length();
         for (int i = 0; i < len; i++) {
             char c = path.charAt(i);
-            if (c == '/' || c == '.' ||
-                    c >= 'a' && c <= 'z' ||
-                    c >= 'A' && c <= 'Z' ||
-                    c >= '0' && c <= '9') {
+            // Ordering in the following test is performance sensitive,
+            // and typically paths have most chars in the a-z range, then
+            // in the symbol range '&'-':' (includes '.', '/' and '0'-'9')
+            // and more rarely in the A-Z range.
+            if (c >= 'a' && c <= 'z' ||
+                c >= '&' && c <= ':' ||
+                c >= 'A' && c <= 'Z') {
                 continue;
             } else if (c > 0x007F || match(c, L_ENCODED, H_ENCODED)) {
                 return i;
@@ -219,9 +224,17 @@
     /**
      * Returns a canonical version of the specified string.
      */
-    public String canonizeString(String file) {
-        int i = 0;
-        int lim = file.length();
+    public static String canonizeString(String file) {
+        int len = file.length();
+        if (len == 0 || (file.indexOf("./") == -1 && file.charAt(len - 1) != '.')) {
+            return file;
+        } else {
+            return doCanonize(file);
+        }
+    }
+
+    private static String doCanonize(String file) {
+        int i, lim;
 
         // Remove embedded /../
         while ((i = file.indexOf("/../")) >= 0) {
--- a/src/java.base/share/classes/sun/net/www/protocol/jar/Handler.java	Thu Feb 15 09:17:56 2018 +0100
+++ b/src/java.base/share/classes/sun/net/www/protocol/jar/Handler.java	Thu Feb 15 16:16:17 2018 +0100
@@ -141,10 +141,9 @@
         // 1. absolute (jar:)
         // 2. relative (i.e. url + foo/bar/baz.ext)
         // 3. anchor-only (i.e. url + #foo), which we already did (refOnly)
-        boolean absoluteSpec = false;
-        if (spec.length() >= 4) {
-            absoluteSpec = spec.substring(0, 4).equalsIgnoreCase("jar:");
-        }
+        boolean absoluteSpec = spec.length() >= 4
+                ? spec.regionMatches(true, 0, "jar:", 0, 4)
+                : false;
         spec = spec.substring(start, limit);
 
         if (absoluteSpec) {
@@ -156,16 +155,14 @@
             int bangSlash = indexOfBangSlash(file);
             String toBangSlash = file.substring(0, bangSlash);
             String afterBangSlash = file.substring(bangSlash);
-            sun.net.www.ParseUtil canonizer = new ParseUtil();
-            afterBangSlash = canonizer.canonizeString(afterBangSlash);
+            afterBangSlash = ParseUtil.canonizeString(afterBangSlash);
             file = toBangSlash + afterBangSlash;
         }
         setURL(url, "jar", "", -1, file, ref);
     }
 
     private String parseAbsoluteSpec(String spec) {
-        URL url = null;
-        int index = -1;
+        int index;
         // check for !/
         if ((index = indexOfBangSlash(spec)) == -1) {
             throw new NullPointerException("no !/ in spec");
@@ -173,7 +170,7 @@
         // test the inner URL
         try {
             String innerSpec = spec.substring(0, index - 1);
-            url = new URL(innerSpec);
+            new URL(innerSpec);
         } catch (MalformedURLException e) {
             throw new NullPointerException("invalid url: " +
                                            spec + " (" + e + ")");
@@ -193,16 +190,16 @@
                                                ": no !/");
             }
             ctxFile = ctxFile.substring(0, bangSlash);
-        }
-        if (!ctxFile.endsWith("/") && (!spec.startsWith("/"))){
+        } else {
             // chop up the last component
             int lastSlash = ctxFile.lastIndexOf('/');
             if (lastSlash == -1) {
                 throw new NullPointerException("malformed " +
                                                "context url:" +
                                                url);
+            } else if (lastSlash < ctxFile.length() - 1) {
+                ctxFile = ctxFile.substring(0, lastSlash + 1);
             }
-            ctxFile = ctxFile.substring(0, lastSlash + 1);
         }
         return (ctxFile + spec);
     }
--- a/src/java.base/share/classes/sun/util/cldr/CLDRCalendarDataProviderImpl.java	Thu Feb 15 09:17:56 2018 +0100
+++ b/src/java.base/share/classes/sun/util/cldr/CLDRCalendarDataProviderImpl.java	Thu Feb 15 16:16:17 2018 +0100
@@ -97,10 +97,11 @@
     }
 
     private static Optional<Integer> retrieveInteger(String src, String region) {
-        return Arrays.stream(src.split(";"))
-            .filter(entry -> entry.contains(region))
-            .map(entry -> entry.substring(0, entry.indexOf(":")))
-            .findAny()
-            .map(Integer::parseInt);
+        int regionIndex = src.indexOf(region);
+        if (regionIndex >= 0) {
+            int start = src.lastIndexOf(';', regionIndex) + 1;
+            return Optional.of(Integer.parseInt(src, start, src.indexOf(':', start), 10));
+        }
+        return Optional.empty();
     }
 }
--- a/src/java.base/windows/classes/sun/nio/ch/PipeImpl.java	Thu Feb 15 09:17:56 2018 +0100
+++ b/src/java.base/windows/classes/sun/nio/ch/PipeImpl.java	Thu Feb 15 16:16:17 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -110,7 +110,7 @@
                     ByteBuffer bb = ByteBuffer.allocate(NUM_SECRET_BYTES);
 
                     // Loopback address
-                    InetAddress lb = InetAddress.getByName("127.0.0.1");
+                    InetAddress lb = InetAddress.getLoopbackAddress();
                     assert(lb.isLoopbackAddress());
                     InetSocketAddress sa = null;
                     for(;;) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/net/www/protocol/jar/CanonicalizationTest.java	Thu Feb 15 16:16:17 2018 +0100
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute 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 8197849
+ * @summary Sanity test the special canonicalization logic for jar resources
+ */
+
+import java.net.URL;
+
+public class CanonicalizationTest {
+    public static void main(String args[]) throws Exception {
+        URL base = new URL("jar:file:/foo!/");
+
+        check(new URL(base, ""), "jar:file:/foo!/");
+        check(new URL(base, "."), "jar:file:/foo!/");
+        check(new URL(base, ".."), "jar:file:/foo!");
+        check(new URL(base, ".x"), "jar:file:/foo!/.x");
+        check(new URL(base, "..x"), "jar:file:/foo!/..x");
+        check(new URL(base, "..."), "jar:file:/foo!/...");
+        check(new URL(base, "foo/."), "jar:file:/foo!/foo/");
+        check(new URL(base, "foo/.."), "jar:file:/foo!/");
+        check(new URL(base, "foo/.x"), "jar:file:/foo!/foo/.x");
+        check(new URL(base, "foo/..x"), "jar:file:/foo!/foo/..x");
+        check(new URL(base, "foo/..."), "jar:file:/foo!/foo/...");
+        check(new URL(base, "foo/./"), "jar:file:/foo!/foo/");
+        check(new URL(base, "foo/../"), "jar:file:/foo!/");
+        check(new URL(base, "foo/.../"), "jar:file:/foo!/foo/.../");
+        check(new URL(base, "foo/../../"), "jar:file:/foo!/");
+        check(new URL(base, "foo/../,,/.."), "jar:file:/foo!/");
+        check(new URL(base, "foo/../."), "jar:file:/foo!/");
+        check(new URL(base, "foo/../.x"), "jar:file:/foo!/.x");
+    }
+
+    private static void check(URL url, String expected) {
+        if (!url.toString().equals(expected)) {
+            throw new AssertionError("Expected " + url + " to equal " + expected);
+        }
+    }
+}