changeset 9155:fbf251b8ef8a jdk8-b127

Merge
author lana
date Wed, 29 Jan 2014 11:11:54 -0800
parents a635c394328c e385bd6f7338
children f777d83e0433
files
diffstat 13 files changed, 556 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/THIRD_PARTY_README	Fri Jan 24 15:08:14 2014 -0800
+++ b/THIRD_PARTY_README	Wed Jan 29 11:11:54 2014 -0800
@@ -1400,7 +1400,7 @@
 -------------------------------------------------------------------------------
 
 %% This notice is provided with respect to Little CMS 2.4, which may be 
-included with OpenJDK 8.
+included with JRE 8, JDK 8, and OpenJDK 8.
 
 --- begin of LICENSE ---
 
--- a/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java	Fri Jan 24 15:08:14 2014 -0800
+++ b/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java	Wed Jan 29 11:11:54 2014 -0800
@@ -114,6 +114,11 @@
                                        Class<?>[] markerInterfaces,
                                        MethodType[] additionalBridges)
             throws LambdaConversionException {
+        if ((caller.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) {
+            throw new LambdaConversionException(String.format(
+                    "Invalid caller: %s",
+                    caller.lookupClass().getName()));
+        }
         this.targetClass = caller.lookupClass();
         this.invokedType = invokedType;
 
@@ -221,6 +226,13 @@
                         String.format("Invalid receiver type %s; not a subtype of implementation type %s",
                                       receiverClass, implDefiningClass));
             }
+
+           Class<?> implReceiverClass = implMethod.type().parameterType(0);
+           if (implReceiverClass != implDefiningClass && !implReceiverClass.isAssignableFrom(receiverClass)) {
+               throw new LambdaConversionException(
+                       String.format("Invalid receiver type %s; not a subtype of implementation receiver type %s",
+                                     receiverClass, implReceiverClass));
+             }
         } else {
             // no receiver
             capturedStart = 0;
@@ -256,11 +268,17 @@
                 (implKind == MethodHandleInfo.REF_newInvokeSpecial)
                   ? implDefiningClass
                   : implMethodType.returnType();
+        Class<?> samReturnType = samMethodType.returnType();
         if (!isAdaptableToAsReturn(actualReturnType, expectedType)) {
             throw new LambdaConversionException(
                     String.format("Type mismatch for lambda return: %s is not convertible to %s",
                                   actualReturnType, expectedType));
         }
+        if (!isAdaptableToAsReturn(expectedType, samReturnType)) {
+            throw new LambdaConversionException(
+                    String.format("Type mismatch for lambda expected return: %s is not convertible to %s",
+                                  expectedType, samReturnType));
+        }
      }
 
     /**
--- a/src/share/classes/java/util/jar/JarFile.java	Fri Jan 24 15:08:14 2014 -0800
+++ b/src/share/classes/java/util/jar/JarFile.java	Wed Jan 29 11:11:54 2014 -0800
@@ -40,6 +40,7 @@
 import sun.security.action.GetPropertyAction;
 import sun.security.util.ManifestEntryVerifier;
 import sun.misc.SharedSecrets;
+import sun.security.util.SignatureFileVerifier;
 
 /**
  * The <code>JarFile</code> class is used to read the contents of a jar file
@@ -364,11 +365,13 @@
             String[] names = getMetaInfEntryNames();
             if (names != null) {
                 for (int i = 0; i < names.length; i++) {
-                    JarEntry e = getJarEntry(names[i]);
-                    if (e == null) {
-                        throw new JarException("corrupted jar file");
-                    }
-                    if (!e.isDirectory()) {
+                    String uname = names[i].toUpperCase(Locale.ENGLISH);
+                    if (MANIFEST_NAME.equals(uname)
+                            || SignatureFileVerifier.isBlockOrSF(uname)) {
+                        JarEntry e = getJarEntry(names[i]);
+                        if (e == null) {
+                            throw new JarException("corrupted jar file");
+                        }
                         if (mev == null) {
                             mev = new ManifestEntryVerifier
                                 (getManifestFromReference());
--- a/src/share/classes/sun/invoke/util/VerifyAccess.java	Fri Jan 24 15:08:14 2014 -0800
+++ b/src/share/classes/sun/invoke/util/VerifyAccess.java	Wed Jan 29 11:11:54 2014 -0800
@@ -118,7 +118,7 @@
                 isSamePackage(defc, lookupClass))
                 return true;
             if ((allowedModes & PROTECTED) != 0 &&
-                isPublicSuperClass(defc, lookupClass))
+                isSuperClass(defc, lookupClass))
                 return true;
             return false;
         case PACKAGE_ONLY:  // That is, zero.  Unmarked member is package-only access.
@@ -140,8 +140,8 @@
                 lookupClass.isAssignableFrom(refc));
     }
 
-    static boolean isPublicSuperClass(Class<?> defc, Class<?> lookupClass) {
-        return isPublic(getClassModifiers(defc)) && defc.isAssignableFrom(lookupClass);
+    static boolean isSuperClass(Class<?> defc, Class<?> lookupClass) {
+        return defc.isAssignableFrom(lookupClass);
     }
 
     static int getClassModifiers(Class<?> c) {
--- a/src/solaris/native/common/jni_util_md.c	Fri Jan 24 15:08:14 2014 -0800
+++ b/src/solaris/native/common/jni_util_md.c	Wed Jan 29 11:11:54 2014 -0800
@@ -23,6 +23,8 @@
  * questions.
  */
 
+#include <string.h>
+
 #include "jni.h"
 #include "jni_util.h"
 #include "dlfcn.h"
@@ -40,7 +42,11 @@
     if (procHandle != NULL) {
         return procHandle;
     }
+#ifdef __APPLE__
+    procHandle = (void*)dlopen(NULL, RTLD_FIRST);
+#else
     procHandle = (void*)dlopen(NULL, RTLD_LAZY);
+#endif
     return procHandle;
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/ProtectedMemberDifferentPackage/Test.java	Wed Jan 29 11:11:54 2014 -0800
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ *
+ */
+
+/**
+ * @test
+ * @bug 8032585
+ * @summary JSR292: IllegalAccessError when attempting to invoke protected method from different package
+ *
+ * @compile p1/T2.java p2/T3.java
+ * @run main/othervm p2.T3
+ */
+public class Test {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/ProtectedMemberDifferentPackage/p1/T2.java	Wed Jan 29 11:11:54 2014 -0800
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ *
+ */
+package p1;
+
+class T1 {
+    protected void m() { System.out.println("T1.m");}
+}
+
+public class T2 extends T1 {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/ProtectedMemberDifferentPackage/p2/T3.java	Wed Jan 29 11:11:54 2014 -0800
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ *
+ */
+package p2;
+
+import p1.T2;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class T3 extends T2 {
+    public static void main(String[] args) throws Throwable {
+        MethodHandles.lookup().findVirtual(T3.class, "m", MethodType.methodType(void.class));
+        System.out.println("TEST PASSED");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/lambda/T8032697.java	Wed Jan 29 11:11:54 2014 -0800
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8032697
+ * @summary Issues with Lambda
+ */
+
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.LambdaConversionException;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import T8032697_anotherpkg.T8032697_A;
+
+public class T8032697 extends T8032697_A {
+
+    interface I {
+        int m();
+    }
+
+    interface IA {
+        int m(T8032697_A x);
+    }
+
+    static MethodHandles.Lookup l;
+    static MethodHandle h;
+    private static MethodType mt(Class<?> k) { return MethodType.methodType(k); }
+    private static MethodType mt(Class<?> k, Class<?> k2) { return MethodType.methodType(k, k2); }
+    private static boolean mf(MethodType mti, MethodType mtf) {
+        try {
+            LambdaMetafactory.metafactory(l, "m", mti,mtf,h,mtf);
+        } catch(LambdaConversionException e) {
+            return true;
+        }
+        return false;
+    }
+
+    public static void main(String[] args) throws Throwable {
+        l = MethodHandles.lookup();
+        h = l.findVirtual(T8032697_A.class, "f", mt(int.class));
+        if (mf(mt(I.class, T8032697.class), mt(int.class))) throw new AssertionError("Error: Should work");
+        if (mf(mt(IA.class), mt(int.class, T8032697.class))) throw new AssertionError("Error: Should work");
+        if (!mf(mt(I.class, T8032697_A.class), mt(int.class))) throw new AssertionError("Error: Should fail");
+        if (!mf(mt(IA.class), mt(int.class, T8032697_A.class))) throw new AssertionError("Error: Should fail");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/lambda/T8032697_anotherpkg/T8032697_A.java	Wed Jan 29 11:11:54 2014 -0800
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package T8032697_anotherpkg;
+
+public class T8032697_A {
+  protected final int f() { return 2; }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/lambda/T8032704.java	Wed Jan 29 11:11:54 2014 -0800
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8032704
+ * @summary Issues with lib perm in Lambda
+ */
+
+import java.io.Closeable;
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.LambdaConversionException;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class T8032704 {
+
+    public static void here() {}
+    static MethodHandle h;
+    private static MethodType mt(Class<?> k) { return MethodType.methodType(k); }
+    private static boolean mf(MethodHandles.Lookup l) {
+        try {
+            LambdaMetafactory.metafactory(l, "close",
+                mt(Closeable.class),mt(void.class),h,mt(void.class));
+        } catch(LambdaConversionException e) {
+            return true;
+        }
+        return false;
+    }
+
+    public static void main(String[] args) throws Throwable {
+        MethodHandles.Lookup ll = MethodHandles.lookup();
+        h = ll.findStatic(T8032704.class, "here", mt(void.class));
+        if (mf(ll)) throw new AssertionError("Error: Should work");
+        if (!mf(MethodHandles.publicLookup())) throw new AssertionError("Error: Should fail - public");
+        if (!mf(ll.in(T8032704other.class))) throw new AssertionError("Error: Should fail - other");
+        if (!mf(ll.in(Thread.class))) throw new AssertionError("Error: Should fail - Thread");
+    }
+}
+
+class T8032704other {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/lambda/T8032711.java	Wed Jan 29 11:11:54 2014 -0800
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8032711
+ * @summary Issue with Lambda in handling
+ */
+
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.LambdaConversionException;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class T8032711 {
+
+    interface I {
+        void m();
+    }
+
+    static void here() {}
+    static MethodHandles.Lookup l;
+    static MethodHandle h;
+    private static MethodType mt(Class<?> k) { return MethodType.methodType(k); }
+    private static boolean mf(Class<?> k) {
+        try {
+            LambdaMetafactory.metafactory(l, "m",
+                mt(I.class),mt(k),h,mt(void.class));
+        } catch(LambdaConversionException e) {
+            return true;
+        }
+        return false;
+    }
+
+    public static void main(String[] args) throws Throwable {
+        l = MethodHandles.lookup();
+        h = l.findStatic(T8032711.class, "here", mt(void.class));
+        if (mf(void.class)) throw new AssertionError("Error: Should work");
+        if (!mf(String.class)) throw new AssertionError("Error: Should fail");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/tools/jarsigner/EntriesOrder.java	Wed Jan 29 11:11:54 2014 -0800
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+/**
+ * @test
+ * @bug 8031572
+ * @summary jarsigner -verify exits with 0 when a jar file is not properly signed
+ */
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.cert.Certificate;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+public class EntriesOrder {
+
+    public static void main(String[] args) throws Exception {
+
+        String[] entries = {
+                "META-INF/",
+                "META-INF/MANIFEST.MF",
+                "META-INF/A.RSA",
+                "META-INF/A.SF",
+                "META-INF/inf",
+                "a"};
+
+        Map<String,byte[]> content = new HashMap<>();
+
+        // We will create a jar containing entries above. Try all permutations
+        // and confirm 1) When opened as a JarFile, we can always get 3 signed
+        // ones (MANIFEST, inf, a), and 2) When opened as a JarInputStream,
+        // when the order is correct (MANIFEST at beginning, followed by RSA/SF,
+        // directory ignored), we can get 2 signed ones (inf, a).
+
+        // Prepares raw files
+        Files.write(Paths.get("a"), "a".getBytes());
+        Files.createDirectory(Paths.get("META-INF/"));
+        Files.write(Paths.get("META-INF/inf"), "inf".getBytes());
+
+        // Pack, sign, and extract to get all files
+        sun.tools.jar.Main m =
+                new sun.tools.jar.Main(System.out, System.err, "jar");
+        if (!m.run("cvf a.jar a META-INF/inf".split(" "))) {
+            throw new Exception("jar creation failed");
+        }
+        sun.security.tools.keytool.Main.main(
+                ("-keystore jks -storepass changeit -keypass changeit -dname" +
+                        " CN=A -alias a -genkeypair -keyalg rsa").split(" "));
+        sun.security.tools.jarsigner.Main.main(
+                "-keystore jks -storepass changeit a.jar a".split(" "));
+        m = new sun.tools.jar.Main(System.out, System.err, "jar");
+        if (!m.run("xvf a.jar".split(" "))) {
+            throw new Exception("jar extraction failed");
+        }
+
+        // Data
+        for (String s: entries) {
+            if (!s.endsWith("/")) {
+                content.put(s, Files.readAllBytes(Paths.get(s)));
+            }
+        }
+
+        // Test
+        for (List<String> perm: Permute(entries)) {
+
+            // Recreate a jar
+            try (ZipOutputStream zos
+                         = new ZipOutputStream(new FileOutputStream("x.jar"))) {
+                for (String e: perm) {
+                    zos.putNextEntry(new ZipEntry(e));
+                    if (Paths.get(e).toFile().isDirectory()) continue;
+                    zos.write(content.get(e));
+                }
+            }
+
+            // Open with JarFile, number of signed entries should be 3.
+            int cc = 0;
+            try (JarFile jf = new JarFile("x.jar")) {
+                Enumeration<JarEntry> jes = jf.entries();
+                while (jes.hasMoreElements()) {
+                    JarEntry je = jes.nextElement();
+                    sun.misc.IOUtils.readFully(jf.getInputStream(je), -1, true);
+                    Certificate[] certs = je.getCertificates();
+                    if (certs != null && certs.length > 0) {
+                        cc++;
+                    }
+                }
+            }
+
+            if (cc != 3) {
+                System.out.println(perm + " - jf - " + cc);
+                throw new Exception();
+            }
+
+            // Open with JarInputStream
+            int signed;
+
+            perm.remove("META-INF/");
+            if (perm.get(0).equals("META-INF/MANIFEST.MF") &&
+                    perm.get(1).contains("/A.") &&
+                    perm.get(2).contains("/A.")) {
+                signed = 2;     // Good order
+            } else {
+                signed = 0;     // Bad order. In this case, the number of signed
+                                // entries is not documented. Just test impl.
+            }
+
+            cc = 0;
+            try (JarInputStream jis
+                         = new JarInputStream(new FileInputStream("x.jar"))) {
+                while (true) {
+                    JarEntry je = jis.getNextJarEntry();
+                    if (je == null) break;
+                    sun.misc.IOUtils.readFully(jis, -1, true);
+                    Certificate[] certs = je.getCertificates();
+                    if (certs != null && certs.length > 0) {
+                        cc++;
+                    }
+                }
+            }
+
+            if (cc != signed) {
+                System.out.println(perm + " - jis - " + cc + " " + signed);
+                throw new Exception();
+            }
+        }
+    }
+
+    // Helper method to return all permutations of an array. Each output can
+    // be altered without damaging the iteration process.
+    static Iterable<List<String>> Permute(String[] entries) {
+        return new Iterable<List<String>>() {
+
+            int s = entries.length;
+            long c = factorial(s) - 1;      // number of permutations
+
+            private long factorial(int n) {
+                return (n == 1) ? 1: (n * factorial(n-1));
+            }
+
+            @Override
+            public Iterator<List<String>> iterator() {
+                return new Iterator<List<String>>() {
+                    @Override
+                    public boolean hasNext() {
+                        return c >= 0;
+                    }
+
+                    @Override
+                    public List<String> next() {
+                        if (c < 0) return null;
+                        List<String> result = new ArrayList<>(s);
+                        LinkedList<String> source = new LinkedList<>(
+                                Arrays.asList(entries));
+                        // Treat c as a integer with different radixes at
+                        // different digits, i.e. at digit 0, radix is s;
+                        // at digit 1, radix is s-1. Thus a s-digit number
+                        // is able to represent s! different values.
+                        long n = c;
+                        for (int i=s; i>=1; i--) {
+                            int x = (int)(n % i);
+                            result.add(source.remove(x));
+                            n = n / i;
+                        }
+                        c--;
+                        return result;
+                    }
+                };
+            }
+        };
+    }
+}