7168401: pack200 does not produce a compatible pack file for JDK7 classes if indy is not present
authorksrini
Tue, 29 May 2012 14:56:48 -0700
changeset 12857 0a5f341c2a28
parent 12856 712335896cc4
child 12858 97e3f3f77254
child 12861 7aa4d0b3a8c7
7168401: pack200 does not produce a compatible pack file for JDK7 classes if indy is not present Reviewed-by: jrose
jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java
jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java
jdk/src/share/classes/com/sun/java/util/jar/pack/ClassReader.java
jdk/src/share/classes/com/sun/java/util/jar/pack/ClassWriter.java
jdk/src/share/classes/com/sun/java/util/jar/pack/Constants.java
jdk/src/share/classes/com/sun/java/util/jar/pack/Package.java
jdk/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java
jdk/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java
jdk/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java
jdk/src/share/classes/com/sun/java/util/jar/pack/PropMap.java
jdk/src/share/classes/com/sun/java/util/jar/pack/Utils.java
jdk/test/tools/pack200/PackageVersionTest.java
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java	Tue May 29 14:56:48 2012 -0700
@@ -653,8 +653,8 @@
             return fixups[0]; // return ref-bearing cookie, if any
         }
 
-        public String layoutForPackageMajver(int majver) {
-            if (majver <= JAVA5_PACKAGE_MAJOR_VERSION) {
+        public String layoutForClassVersion(Package.Version vers) {
+            if (vers.lessThan(JAVA6_MAX_CLASS_VERSION)) {
                 // Disallow layout syntax in the oldest protocol version.
                 return expandCaseDashNotation(layout);
             }
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java	Tue May 29 14:56:48 2012 -0700
@@ -74,30 +74,27 @@
 
     abstract protected Index getCPIndex(byte tag);
 
-    // Local copy of package version.
-    private int packageMajver = -1;
+    // Local copy of highest class version.
+    private Package.Version highestClassVersion = null;
 
     /** Call this exactly once, early, to specify the archive major version. */
-    public void initPackageMajver(int packageMajver) throws IOException {
-        assert(packageMajver > 0 && packageMajver < 0x10000);
-        if (this.packageMajver > 0) {
+    public void initHighestClassVersion(Package.Version highestClassVersion) throws IOException {
+        if (this.highestClassVersion != null) {
             throw new IOException(
-                "Package majver is already initialized to " + this.packageMajver+
-                "; new setting is " + packageMajver);
+                "Highest class major version is already initialized to " +
+                this.highestClassVersion + "; new setting is " + highestClassVersion);
         }
-        this.packageMajver = packageMajver;
-        adjustToMajver();
+        this.highestClassVersion = highestClassVersion;
+        adjustToClassVersion();
     }
-    public int getPackageMajver() {
-        if (packageMajver < 0) {
-            throw new RuntimeException("Package majver not yet initialized");
-        }
-        return packageMajver;
+
+    public Package.Version getHighestClassVersion() {
+        return highestClassVersion;
     }
 
     private final boolean isReader = this instanceof PackageReader;
-    protected BandStructure() {
-    }
+
+    protected BandStructure() {}
 
     final static Coding BYTE1 = Coding.of(1,256);
 
@@ -1866,20 +1863,12 @@
         attrClassFileVersionMask = (1<<CLASS_ATTR_ClassFile_version);
     }
 
-    private void adjustToMajver() throws IOException {
-        if (getPackageMajver() < JAVA6_PACKAGE_MAJOR_VERSION) {
+    private void adjustToClassVersion() throws IOException {
+        if (getHighestClassVersion().lessThan(JAVA6_MAX_CLASS_VERSION)) {
             if (verbose > 0)  Utils.log.fine("Legacy package version");
             // Revoke definition of pre-1.6 attribute type.
             undefineAttribute(CODE_ATTR_StackMapTable, ATTR_CONTEXT_CODE);
         }
-        if (getPackageMajver() < JAVA7_PACKAGE_MAJOR_VERSION) {
-            if (testBit(archiveOptions, AO_HAVE_CP_EXTRAS))
-                // this bit was reserved for future use in previous versions
-                throw new IOException("Format bits for Java 7 must be zero in previous releases");
-        }
-        if (testBit(archiveOptions, AO_UNUSED_MBZ)) {
-            throw new IOException("High archive option bits are reserved and must be zero: "+Integer.toHexString(archiveOptions));
-        }
     }
 
     protected void initAttrIndexLimit() {
@@ -1942,21 +1931,14 @@
         }
     }
 
-    protected Attribute makeClassFileVersionAttr(int minver, int majver) {
-        byte[] bytes = {
-            (byte)(minver >> 8), (byte)minver,
-            (byte)(majver >> 8), (byte)majver
-        };
-        return attrClassFileVersion.addContent(bytes);
+    protected Attribute makeClassFileVersionAttr(Package.Version ver) {
+        return attrClassFileVersion.addContent(ver.asBytes());
     }
 
-    protected short[] parseClassFileVersionAttr(Attribute attr) {
+    protected Package.Version parseClassFileVersionAttr(Attribute attr) {
         assert(attr.layout() == attrClassFileVersion);
         assert(attr.size() == 4);
-        byte[] bytes = attr.bytes();
-        int minver = ((bytes[0] & 0xFF) << 8) | (bytes[1] & 0xFF);
-        int majver = ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);
-        return new short[]{ (short) minver, (short) majver };
+        return Package.Version.of(attr.bytes());
     }
 
     private boolean assertBandOKForElems(Band[] ab, Attribute.Layout.Element[] elems) {
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/ClassReader.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/ClassReader.java	Tue May 29 14:56:48 2012 -0700
@@ -174,27 +174,31 @@
                 ("Bad magic number in class file "
                  +Integer.toHexString(cls.magic),
                  ATTR_CONTEXT_CLASS, "magic-number", "pass");
-        cls.minver = (short) readUnsignedShort();
-        cls.majver = (short) readUnsignedShort();
+        int minver = (short) readUnsignedShort();
+        int majver = (short) readUnsignedShort();
+        cls.version = Package.Version.of(majver, minver);
+
         //System.out.println("ClassFile.version="+cls.majver+"."+cls.minver);
-        String bad = checkVersion(cls.majver, cls.minver);
+        String bad = checkVersion(cls.version);
         if (bad != null) {
             throw new Attribute.FormatException
                 ("classfile version too "+bad+": "
-                 +cls.majver+"."+cls.minver+" in "+cls.file,
+                 +cls.version+" in "+cls.file,
                  ATTR_CONTEXT_CLASS, "version", "pass");
         }
     }
 
-    private String checkVersion(int majver, int minver) {
-        if (majver < pkg.min_class_majver ||
-            (majver == pkg.min_class_majver &&
-             minver < pkg.min_class_minver)) {
+    private String checkVersion(Package.Version ver) {
+        int majver = ver.major;
+        int minver = ver.minor;
+        if (majver < pkg.minClassVersion.major ||
+            (majver == pkg.minClassVersion.major &&
+             minver < pkg.minClassVersion.minor)) {
             return "small";
         }
-        if (majver > pkg.max_class_majver ||
-            (majver == pkg.max_class_majver &&
-             minver > pkg.max_class_minver)) {
+        if (majver > pkg.maxClassVersion.major ||
+            (majver == pkg.maxClassVersion.major &&
+             minver > pkg.maxClassVersion.minor)) {
             return "large";
         }
         return null;  // OK
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/ClassWriter.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/ClassWriter.java	Tue May 29 14:56:48 2012 -0700
@@ -113,8 +113,8 @@
 
     void writeMagicNumbers() throws IOException {
         writeInt(cls.magic);
-        writeShort(cls.minver);
-        writeShort(cls.majver);
+        writeShort(cls.version.minor);
+        writeShort(cls.version.major);
     }
 
     void writeConstantPool() throws IOException {
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Constants.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Constants.java	Tue May 29 14:56:48 2012 -0700
@@ -43,30 +43,40 @@
         1.0 to 1.3.X 45,3
         1.4 to 1.4.X 46,0
         1.5 to 1.5.X 49,0
-        1.6 to 1.5.x 50,0 NOTE Assumed for now
+        1.6 to 1.5.x 50,0
+        1.7 to 1.6.x 51,0
     */
 
-    public final static short JAVA_MIN_CLASS_MAJOR_VERSION = 45;
-    public final static short JAVA_MIN_CLASS_MINOR_VERSION = 03;
+    public final static Package.Version JAVA_MIN_CLASS_VERSION =
+            Package.Version.of(45, 03);
 
-    public final static short JAVA5_MAX_CLASS_MAJOR_VERSION = 49;
-    public final static short JAVA5_MAX_CLASS_MINOR_VERSION = 0;
+    public final static Package.Version JAVA5_MAX_CLASS_VERSION =
+            Package.Version.of(49, 00);
 
-    public final static short JAVA6_MAX_CLASS_MAJOR_VERSION = 50;
-    public final static short JAVA6_MAX_CLASS_MINOR_VERSION = 0;
+    public final static Package.Version JAVA6_MAX_CLASS_VERSION =
+            Package.Version.of(50, 00);
 
-    public final static short JAVA7_MAX_CLASS_MAJOR_VERSION = 51;
-    public final static short JAVA7_MAX_CLASS_MINOR_VERSION = 0;
+    public final static Package.Version JAVA7_MAX_CLASS_VERSION =
+            Package.Version.of(51, 00);
 
     public final static int JAVA_PACKAGE_MAGIC = 0xCAFED00D;
-    public final static int JAVA5_PACKAGE_MAJOR_VERSION = 150;
-    public final static int JAVA5_PACKAGE_MINOR_VERSION = 7;
+
+    public final static Package.Version JAVA5_PACKAGE_VERSION =
+            Package.Version.of(150, 7);
+
+    public final static Package.Version JAVA6_PACKAGE_VERSION =
+            Package.Version.of(160, 1);
 
-    public final static int JAVA6_PACKAGE_MAJOR_VERSION = 160;
-    public final static int JAVA6_PACKAGE_MINOR_VERSION = 1;
+    public final static Package.Version JAVA7_PACKAGE_VERSION =
+            Package.Version.of(170, 1);
 
-    public final static int JAVA7_PACKAGE_MAJOR_VERSION = 170;
-    public final static int JAVA7_PACKAGE_MINOR_VERSION = 1;
+    // upper limit, should point to the latest class version
+    public final static Package.Version JAVA_MAX_CLASS_VERSION =
+            JAVA7_MAX_CLASS_VERSION;
+
+    // upper limit should point to the latest package version, for version info!.
+    public final static Package.Version MAX_PACKAGE_VERSION =
+            JAVA7_PACKAGE_VERSION;
 
     public final static int CONSTANT_POOL_INDEX_LIMIT  = 0x10000;
     public final static int CONSTANT_POOL_NARROW_LIMIT = 0x00100;
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Package.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Package.java	Tue May 29 14:56:48 2012 -0700
@@ -25,6 +25,7 @@
 
 package com.sun.java.util.jar.pack;
 
+import java.util.jar.Pack200;
 import com.sun.java.util.jar.pack.Attribute.Layout;
 import com.sun.java.util.jar.pack.ConstantPool.ClassEntry;
 import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry;
@@ -67,46 +68,58 @@
             verbose = pmap.getInteger(Utils.DEBUG_VERBOSE);
     }
 
-    int magic;
-    int package_minver;
-    int package_majver;
+    final int magic = JAVA_PACKAGE_MAGIC;
 
     int default_modtime = NO_MODTIME;
     int default_options = 0;  // FO_DEFLATE_HINT
 
-    short default_class_majver = -1; // fill in later
-    short default_class_minver = 0;  // fill in later
+    Version defaultClassVersion = null;
 
     // These fields can be adjusted by driver properties.
-    short min_class_majver = JAVA_MIN_CLASS_MAJOR_VERSION;
-    short min_class_minver = JAVA_MIN_CLASS_MINOR_VERSION;
-    short max_class_majver = JAVA7_MAX_CLASS_MAJOR_VERSION;
-    short max_class_minver = JAVA7_MAX_CLASS_MINOR_VERSION;
+    final Version minClassVersion;
+    final Version maxClassVersion;
+    // null, indicates that consensus rules during package write
+    final Version packageVersion;
 
-    short observed_max_class_majver = min_class_majver;
-    short observed_max_class_minver = min_class_minver;
+    Version observedHighestClassVersion = null;
+
 
     // What constants are used in this unit?
     ConstantPool.IndexGroup cp = new ConstantPool.IndexGroup();
 
-    Package() {
-        magic          = JAVA_PACKAGE_MAGIC;
-        package_minver = -1;  // fill in later
-        package_majver = 0;   // fill in later
+    /*
+     * typically used by the PackageReader to set the defaults, in which
+     * case we take the defaults.
+     */
+    public Package() {
+        minClassVersion = JAVA_MIN_CLASS_VERSION;
+        maxClassVersion = JAVA_MAX_CLASS_VERSION;
+        packageVersion = null;
     }
 
-    public
-    void reset() {
+
+    /*
+     * Typically used by the PackerImpl during before packing, the defaults are
+     * overridden by the users preferences.
+     */
+    public Package(Version minClassVersion, Version maxClassVersion, Version packageVersion) {
+        // Fill in permitted range of major/minor version numbers.
+        this.minClassVersion = minClassVersion == null
+                ? JAVA_MIN_CLASS_VERSION
+                : minClassVersion;
+        this.maxClassVersion = maxClassVersion == null
+                ? JAVA_MAX_CLASS_VERSION
+                : maxClassVersion;
+        this.packageVersion  = packageVersion;
+    }
+
+
+    public void reset() {
         cp = new ConstantPool.IndexGroup();
         classes.clear();
         files.clear();
         BandStructure.nextSeqForDebug = 0;
-        package_minver = -1;  // fill in later
-        package_majver = 0;   // fill in later
-    }
-
-    int getPackageVersion() {
-        return (package_majver << 16) + package_minver;
+        observedHighestClassVersion = null;
     }
 
     // Special empty versions of Code and InnerClasses, used for markers.
@@ -128,73 +141,31 @@
         attrDefs = Collections.unmodifiableMap(ad);
     }
 
-    int getDefaultClassVersion() {
-        return (default_class_majver << 16) + (char)default_class_minver;
+    Version getDefaultClassVersion() {
+        return defaultClassVersion;
     }
 
     /** Return the highest version number of all classes,
      *  or 0 if there are no classes.
      */
-    int getHighestClassVersion() {
-        int res = 0;  // initial low value
+    private void setHighestClassVersion() {
+        if (observedHighestClassVersion != null)
+            return;
+        Version res = JAVA_MIN_CLASS_VERSION;  // initial low value
         for (Class cls : classes) {
-            int ver = cls.getVersion();
-            if (res < ver)  res = ver;
+            Version ver = cls.getVersion();
+            if (res.lessThan(ver))  res = ver;
         }
-        return res;
+        observedHighestClassVersion = res;
     }
 
-    /** Convenience function to choose an archive version based
-     *  on the class file versions observed within the archive.
-     */
-    void choosePackageVersion() {
-        assert(package_majver <= 0);  // do not call this twice
-        int classver = getHighestClassVersion();
-        if (classver == 0 || (classver >>> 16) < JAVA6_MAX_CLASS_MAJOR_VERSION) {
-            // There are only old classfiles in this segment or resources
-            package_majver = JAVA5_PACKAGE_MAJOR_VERSION;
-            package_minver = JAVA5_PACKAGE_MINOR_VERSION;
-        } else if ((classver >>> 16) == JAVA6_MAX_CLASS_MAJOR_VERSION) {
-            package_majver = JAVA6_PACKAGE_MAJOR_VERSION;
-            package_minver = JAVA6_PACKAGE_MINOR_VERSION;
-        } else {
-            // Normal case.  Use the newest archive format, when available
-            package_majver = JAVA7_PACKAGE_MAJOR_VERSION;
-            package_minver = JAVA7_PACKAGE_MINOR_VERSION;
-        }
+    Version getHighestClassVersion() {
+        setHighestClassVersion();
+        return observedHighestClassVersion;
     }
 
     // What Java classes are in this unit?
 
-    // Fixed 6211177, converted to throw IOException
-    void checkVersion() throws IOException {
-        if (magic != JAVA_PACKAGE_MAGIC) {
-            String gotMag = Integer.toHexString(magic);
-            String expMag = Integer.toHexString(JAVA_PACKAGE_MAGIC);
-            throw new IOException("Unexpected package magic number: got "+gotMag+"; expected "+expMag);
-        }
-        int[] majminFound = null;
-        for (int[] majmin : new int[][]{
-                { JAVA7_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION },
-                { JAVA6_PACKAGE_MAJOR_VERSION, JAVA6_PACKAGE_MINOR_VERSION },
-                { JAVA5_PACKAGE_MAJOR_VERSION, JAVA5_PACKAGE_MINOR_VERSION }
-            }) {
-            if (package_majver == majmin[0] && package_minver == majmin[1]) {
-                majminFound = majmin;
-                break;
-            }
-        }
-        if (majminFound == null) {
-            String gotVer = package_majver+"."+package_minver;
-            String expVer = JAVA7_PACKAGE_MAJOR_VERSION+"."+JAVA7_PACKAGE_MINOR_VERSION+
-                            " OR "+
-                            JAVA6_PACKAGE_MAJOR_VERSION+"."+JAVA6_PACKAGE_MINOR_VERSION+
-                            " OR "+
-                            JAVA5_PACKAGE_MAJOR_VERSION+"."+JAVA5_PACKAGE_MINOR_VERSION;
-            throw new IOException("Unexpected package minor version: got "+gotVer+"; expected "+expVer);
-        }
-    }
-
     ArrayList<Package.Class> classes = new ArrayList<>();
 
     public List<Package.Class> getClasses() {
@@ -210,7 +181,7 @@
 
         // File header
         int magic;
-        short minver, majver;
+        Version version;
 
         // Local constant pool (one-way mapping of index => package cp).
         Entry[] cpMap;
@@ -231,8 +202,7 @@
 
         Class(int flags, ClassEntry thisClass, ClassEntry superClass, ClassEntry[] interfaces) {
             this.magic      = JAVA_MAGIC;
-            this.minver     = default_class_minver;
-            this.majver     = default_class_majver;
+            this.version    = defaultClassVersion;
             this.flags      = flags;
             this.thisClass  = thisClass;
             this.superClass = superClass;
@@ -254,11 +224,8 @@
             return thisClass.stringValue();
         }
 
-        int getVersion() {
-            return (majver << 16) + (char)minver;
-        }
-        String getVersionString() {
-            return versionStringOf(majver, minver);
+        Version getVersion() {
+            return this.version;
         }
 
         // Note:  equals and hashCode are identity-based.
@@ -1182,13 +1149,6 @@
         }
     }
 
-    public static String versionStringOf(int majver, int minver) {
-        return majver+"."+minver;
-    }
-    public static String versionStringOf(int version) {
-        return versionStringOf(version >>> 16, (char)version);
-    }
-
     public void stripConstantFields() {
         for (Class c : classes) {
             for (Iterator<Class.Field> j = c.fields.iterator(); j.hasNext(); ) {
@@ -1342,4 +1302,75 @@
     static final List<Class.Field> noFields = Arrays.asList(new Class.Field[0]);
     static final List<Class.Method> noMethods = Arrays.asList(new Class.Method[0]);
     static final List<InnerClass> noInnerClasses = Arrays.asList(new InnerClass[0]);
+
+    protected static final class Version {
+
+        public final short major;
+        public final short minor;
+
+        private Version(short major, short minor) {
+            this.major = major;
+            this.minor = minor;
+        }
+
+        public String toString() {
+            return major + "." + minor;
+        }
+
+        public boolean equals(Object that) {
+            return that instanceof Version
+                    && major == ((Version)that).major
+                    && minor == ((Version)that).minor;
+        }
+
+        public int intValue() {
+            return (major << 16) + minor;
+        }
+
+        public int hashCode() {
+            return (major << 16) + 7 + minor;
+        }
+
+        public static Version of(int major, int minor) {
+            return new Version((short)major, (short)minor);
+        }
+
+        public static Version of(byte[] bytes) {
+           int minor = ((bytes[0] & 0xFF) << 8) | (bytes[1] & 0xFF);
+           int major = ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);
+           return new Version((short)major, (short)minor);
+        }
+
+        public static Version of(int major_minor) {
+            short minor = (short)major_minor;
+            short major = (short)(major_minor >>> 16);
+            return new Version(major, minor);
+        }
+
+        public static Version makeVersion(PropMap props, String partialKey) {
+            int min = props.getInteger(Utils.COM_PREFIX
+                    + partialKey + ".minver", -1);
+            int maj = props.getInteger(Utils.COM_PREFIX
+                    + partialKey + ".majver", -1);
+            return min >= 0 && maj >= 0 ? Version.of(maj, min) : null;
+        }
+        public byte[] asBytes() {
+            byte[] bytes = {
+                (byte) (minor >> 8), (byte) minor,
+                (byte) (major >> 8), (byte) major
+            };
+            return bytes;
+        }
+        public int compareTo(Version that) {
+            return this.intValue() - that.intValue();
+        }
+
+        public boolean lessThan(Version that) {
+            return compareTo(that) < 0 ;
+        }
+
+        public boolean greaterThan(Version that) {
+            return compareTo(that) > 0 ;
+        }
+    }
 }
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java	Tue May 29 14:56:48 2012 -0700
@@ -60,6 +60,7 @@
     Package pkg;
     byte[] bytes;
     LimitedBuffer in;
+    Package.Version packageVersion;
 
     PackageReader(Package pkg, InputStream in) throws IOException {
         this.pkg = pkg;
@@ -220,7 +221,6 @@
     final static int MAGIC_BYTES = 4;
 
     void readArchiveMagic() throws IOException {
-
         // Read a minimum of bytes in the first gulp.
         in.setReadLimit(MAGIC_BYTES + AH_LENGTH_MIN);
 
@@ -230,10 +230,38 @@
         archive_magic.readFrom(in);
 
         // read and check magic numbers:
-        pkg.magic = getMagicInt32();
+        int magic = getMagicInt32();
+        if (pkg.magic != magic) {
+            throw new IOException("Unexpected package magic number: got "
+                    + magic + "; expected " + pkg.magic);
+        }
         archive_magic.doneDisbursing();
     }
 
+     // Fixed 6211177, converted to throw IOException
+    void checkArchiveVersion() throws IOException {
+        Package.Version versionFound = null;
+        for (Package.Version v : new Package.Version[] {
+                JAVA7_PACKAGE_VERSION,
+                JAVA6_PACKAGE_VERSION,
+                JAVA5_PACKAGE_VERSION
+            }) {
+            if (packageVersion.equals(v)) {
+                versionFound = v;
+                break;
+            }
+        }
+        if (versionFound == null) {
+            String expVer = JAVA7_PACKAGE_VERSION.toString()
+                            + " OR "
+                            + JAVA6_PACKAGE_VERSION.toString()
+                            + " OR "
+                            + JAVA5_PACKAGE_VERSION.toString();
+            throw new IOException("Unexpected package minor version: got "
+                    +  packageVersion.toString() + "; expected " + expVer);
+        }
+    }
+
     void readArchiveHeader() throws IOException {
         //  archive_header:
         //        #archive_minver :UNSIGNED5[1]
@@ -264,10 +292,11 @@
         archive_header_0.expectLength(AH_LENGTH_0);
         archive_header_0.readFrom(in);
 
-        pkg.package_minver = archive_header_0.getInt();
-        pkg.package_majver = archive_header_0.getInt();
-        pkg.checkVersion();
-        this.initPackageMajver(pkg.package_majver);
+        int minver = archive_header_0.getInt();
+        int majver = archive_header_0.getInt();
+        packageVersion = Package.Version.of(majver, minver);
+        checkArchiveVersion();
+        this.initHighestClassVersion(JAVA7_MAX_CLASS_VERSION);
 
         archiveOptions = archive_header_0.getInt();
         archive_header_0.doneDisbursing();
@@ -324,8 +353,9 @@
 
         numInnerClasses = archive_header_1.getInt();
 
-        pkg.default_class_minver = (short) archive_header_1.getInt();
-        pkg.default_class_majver = (short) archive_header_1.getInt();
+        minver = (short) archive_header_1.getInt();
+        majver = (short) archive_header_1.getInt();
+        pkg.defaultClassVersion = Package.Version.of(majver, minver);
         numClasses = archive_header_1.getInt();
 
         archive_header_1.doneDisbursing();
@@ -414,7 +444,7 @@
     }
 
     void checkLegacy(String bandname) {
-        if (this.pkg.package_majver < JAVA7_PACKAGE_MAJOR_VERSION) {
+        if (packageVersion.lessThan(JAVA7_PACKAGE_VERSION)) {
             throw new RuntimeException("unexpected band " + bandname);
         }
     }
@@ -947,9 +977,9 @@
                                                             name.stringValue(),
                                                             layout.stringValue());
                 // Check layout string for Java 6 extensions.
-                String pvLayout = def.layoutForPackageMajver(getPackageMajver());
+                String pvLayout = def.layoutForClassVersion(getHighestClassVersion());
                 if (!pvLayout.equals(def.layout())) {
-                    throw new IOException("Bad attribute layout in version 150 archive: "+def.layout());
+                    throw new IOException("Bad attribute layout in archive: "+def.layout());
                 }
                 this.setAttributeLayoutIndex(def, index);
                 if (dump != null)  dump.println(index+" "+def);
@@ -1140,12 +1170,9 @@
         Attribute retroVersion = cls.getAttribute(attrClassFileVersion);
         if (retroVersion != null) {
             cls.removeAttribute(retroVersion);
-            short[] minmajver = parseClassFileVersionAttr(retroVersion);
-            cls.minver = minmajver[0];
-            cls.majver = minmajver[1];
+            cls.version = parseClassFileVersionAttr(retroVersion);
         } else {
-            cls.minver = pkg.default_class_minver;
-            cls.majver = pkg.default_class_majver;
+            cls.version = pkg.defaultClassVersion;
         }
 
         // Replace null SourceFile by "obvious" string.
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java	Tue May 29 14:56:48 2012 -0700
@@ -49,12 +49,13 @@
 class PackageWriter extends BandStructure {
     Package pkg;
     OutputStream finalOut;
+    Package.Version packageVersion;
 
     PackageWriter(Package pkg, OutputStream out) throws IOException {
         this.pkg = pkg;
         this.finalOut = out;
-        // Caller has specified archive version in the package:
-        initPackageMajver(pkg.package_majver);
+        // Caller has specified maximum class file version in the package:
+        initHighestClassVersion(pkg.getHighestClassVersion());
     }
 
     void write() throws IOException {
@@ -118,6 +119,57 @@
         collectInnerClasses();
     }
 
+    /*
+     * Convenience function to choose an archive version based
+     * on the class file versions observed within the archive
+     * or set the user defined version preset via properties.
+     */
+    void chooseDefaultPackageVersion() throws IOException {
+        if (pkg.packageVersion != null) {
+            packageVersion = pkg.packageVersion;
+            if (verbose > 0) {
+                Utils.log.info("package version overridden with: "
+                                + packageVersion);
+            }
+            return;
+        }
+
+        Package.Version highV = getHighestClassVersion();
+        // set the package version now
+        if (highV.lessThan(JAVA6_MAX_CLASS_VERSION)) {
+            // There are only old classfiles in this segment or resources
+            packageVersion = JAVA5_PACKAGE_VERSION;
+        } else if (highV.equals(JAVA6_MAX_CLASS_VERSION) ||
+                (highV.equals(JAVA7_MAX_CLASS_VERSION) && !pkg.cp.haveExtraTags())) {
+            // force down the package version if we have jdk7 classes without
+            // any Indy references, this is because jdk7 class file (52.0) without
+            // Indy is identical to jdk6 class file (51.0).
+            packageVersion = JAVA6_PACKAGE_VERSION;
+        } else {
+            // Normal case.  Use the newest archive format, when available
+            packageVersion = JAVA7_PACKAGE_VERSION;
+        }
+
+        if (verbose > 0) {
+            Utils.log.info("Highest version class file: " + highV
+                    + " package version: " + packageVersion);
+        }
+    }
+
+    void checkVersion() throws IOException {
+        assert(packageVersion != null);
+
+        if (packageVersion.lessThan(JAVA7_PACKAGE_VERSION)) {
+            // this bit was reserved for future use in previous versions
+            if (testBit(archiveOptions, AO_HAVE_CP_EXTRAS)) {
+                throw new IOException("Format bits for Java 7 must be zero in previous releases");
+            }
+        }
+        if (testBit(archiveOptions, AO_UNUSED_MBZ)) {
+            throw new IOException("High archive option bits are reserved and must be zero: " + Integer.toHexString(archiveOptions));
+        }
+    }
+
     void setArchiveOptions() {
         // Decide on some archive options early.
         // Does not decide on: AO_HAVE_SPECIAL_FORMATS,
@@ -168,11 +220,11 @@
             }
         }
         // Decide on default version number (majority rule).
-        Map<Integer, int[]> verCounts = new HashMap<>();
+        Map<Package.Version, int[]> verCounts = new HashMap<>();
         int bestCount = 0;
-        int bestVersion = -1;
+        Package.Version bestVersion = null;
         for (Class cls : pkg.classes) {
-            int version = cls.getVersion();
+            Package.Version version = cls.getVersion();
             int[] var = verCounts.get(version);
             if (var == null) {
                 var = new int[1];
@@ -186,28 +238,22 @@
             }
         }
         verCounts.clear();
-        if (bestVersion == -1)  bestVersion = 0;  // degenerate case
-        int bestMajver = (char)(bestVersion >>> 16);
-        int bestMinver = (char)(bestVersion);
-        pkg.default_class_majver = (short) bestMajver;
-        pkg.default_class_minver = (short) bestMinver;
-        String bestVerStr = Package.versionStringOf(bestMajver, bestMinver);
+        if (bestVersion == null)  bestVersion = JAVA_MIN_CLASS_VERSION;  // degenerate case
+        pkg.defaultClassVersion = bestVersion;
         if (verbose > 0)
-           Utils.log.info("Consensus version number in segment is "+bestVerStr);
+           Utils.log.info("Consensus version number in segment is " + bestVersion);
         if (verbose > 0)
-            Utils.log.info("Highest version number in segment is "+
-                           Package.versionStringOf(pkg.getHighestClassVersion()));
+            Utils.log.info("Highest version number in segment is "
+                            + pkg.getHighestClassVersion());
 
         // Now add explicit pseudo-attrs. to classes with odd versions.
         for (Class cls : pkg.classes) {
-            if (cls.getVersion() != bestVersion) {
-                Attribute a = makeClassFileVersionAttr(cls.minver, cls.majver);
+            if (!cls.getVersion().equals(bestVersion)) {
+                Attribute a = makeClassFileVersionAttr(cls.getVersion());
                 if (verbose > 1) {
-                    String clsVer = cls.getVersionString();
-                    String pkgVer = bestVerStr;
-                    Utils.log.fine("Version "+clsVer+" of "+cls
-                                     +" doesn't match package version "
-                                     +pkgVer);
+                    Utils.log.fine("Version "+cls.getVersion() + " of " + cls
+                                     + " doesn't match package version "
+                                     + bestVersion);
                 }
                 // Note:  Does not add in "natural" order.  (Who cares?)
                 cls.addAttribute(a);
@@ -252,7 +298,7 @@
     }
 
     void writeFileHeader() throws IOException {
-        pkg.checkVersion();
+        chooseDefaultPackageVersion();
         writeArchiveMagic();
         writeArchiveHeader();
     }
@@ -322,12 +368,13 @@
         if (haveCPExtra)
             headerSizeForDebug += AH_CP_EXTRA_LEN;
 
-        assert(pkg.package_majver > 0);  // caller must specify!
-        archive_header_0.putInt(pkg.package_minver);
-        archive_header_0.putInt(pkg.package_majver);
+        // the archiveOptions are all initialized, sanity check now!.
+        checkVersion();
+
+        archive_header_0.putInt(packageVersion.minor);
+        archive_header_0.putInt(packageVersion.major);
         if (verbose > 0)
-            Utils.log.info("Package Version for this segment:"+
-                           Package.versionStringOf(pkg.getPackageVersion()));
+            Utils.log.info("Package Version for this segment:" + packageVersion);
         archive_header_0.putInt(archiveOptions); // controls header format
         assert(archive_header_0.length() == AH_LENGTH_0);
 
@@ -361,8 +408,8 @@
         writeConstantPoolCounts(haveNumbers, haveCPExtra);
 
         archive_header_1.putInt(pkg.getAllInnerClasses().size());
-        archive_header_1.putInt(pkg.default_class_minver);
-        archive_header_1.putInt(pkg.default_class_majver);
+        archive_header_1.putInt(pkg.defaultClassVersion.minor);
+        archive_header_1.putInt(pkg.defaultClassVersion.major);
         archive_header_1.putInt(pkg.classes.size());
 
         // Sanity:  Make sure we came out to 29 (less optional fields):
@@ -892,7 +939,7 @@
                 if (predefIndex == null) {
                     // Make sure the package CP can name the local attribute.
                     Entry ne = ConstantPool.getUtf8Entry(def.name());
-                    String layout = def.layoutForPackageMajver(getPackageMajver());
+                    String layout = def.layoutForClassVersion(getHighestClassVersion());
                     Entry le = ConstantPool.getUtf8Entry(layout);
                     requiredEntries.add(ne);
                     requiredEntries.add(le);
@@ -988,7 +1035,7 @@
                 assert((header & ADH_CONTEXT_MASK) == def.ctype());
                 attr_definition_headers.putByte(header);
                 attr_definition_name.putRef(ConstantPool.getUtf8Entry(def.name()));
-                String layout = def.layoutForPackageMajver(getPackageMajver());
+                String layout = def.layoutForClassVersion(getHighestClassVersion());
                 attr_definition_layout.putRef(ConstantPool.getUtf8Entry(layout));
                 // Check that we are transmitting that correct attribute index:
                 boolean debug = false;
@@ -1542,8 +1589,8 @@
                         break;
                     default:
                         // CONSTANT_MethodHandle, etc.
-                        if (getPackageMajver() < JAVA7_PACKAGE_MAJOR_VERSION) {
-                            throw new IOException("bad package major version for Java 7 ldc");
+                        if (getHighestClassVersion().lessThan(JAVA7_MAX_CLASS_VERSION)) {
+                            throw new IOException("bad class file major version for Java 7 ldc");
                         }
                         bc_which = bc_loadablevalueref;
                         switch (bc) {
@@ -1581,8 +1628,8 @@
                     // Make sure the discarded bytes are sane:
                     assert(i.getConstant() == (1+((MemberEntry)ref).descRef.typeRef.computeSize(true)) << 8);
                 } else if (bc == _invokedynamic) {
-                    if (getPackageMajver() < JAVA7_PACKAGE_MAJOR_VERSION) {
-                        throw new IOException("bad package major version for Java 7 invokedynamic");
+                    if (getHighestClassVersion().lessThan(JAVA7_MAX_CLASS_VERSION)) {
+                        throw new IOException("bad class major version for Java 7 invokedynamic");
                     }
                     assert(i.getLength() == 5);
                     assert(i.getConstant() == 0);  // last 2 bytes MBZ
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java	Tue May 29 14:56:48 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -164,8 +164,11 @@
             if (verbose > 0) Utils.log.info(props.toString());
         }
 
-        // Here's where the bits are collected before getting packed:
-        final Package pkg = new Package();
+        // Here's where the bits are collected before getting packed, we also
+        // initialize the version numbers now.
+        final Package pkg = new Package(Package.Version.makeVersion(props, "min.class"),
+                                        Package.Version.makeVersion(props, "max.class"),
+                                        Package.Version.makeVersion(props, "package"));
 
         final String unknownAttrCommand;
         {
@@ -280,23 +283,6 @@
         }
 
         {
-            // Fill in permitted range of major/minor version numbers.
-            int ver;
-            if ((ver = props.getInteger(Utils.COM_PREFIX+"min.class.majver")) != 0)
-                pkg.min_class_majver = (short) ver;
-            if ((ver = props.getInteger(Utils.COM_PREFIX+"min.class.minver")) != 0)
-                pkg.min_class_minver = (short) ver;
-            if ((ver = props.getInteger(Utils.COM_PREFIX+"max.class.majver")) != 0)
-                pkg.max_class_majver = (short) ver;
-            if ((ver = props.getInteger(Utils.COM_PREFIX+"max.class.minver")) != 0)
-                pkg.max_class_minver = (short) ver;
-            if ((ver = props.getInteger(Utils.COM_PREFIX+"package.minver")) != 0)
-                pkg.package_minver = (short) ver;
-            if ((ver = props.getInteger(Utils.COM_PREFIX+"package.majver")) != 0)
-                pkg.package_majver = (short) ver;
-        }
-
-        {
             // Hook for testing:  Forces use of special archive modes.
             int opt = props.getInteger(Utils.COM_PREFIX+"archive.options");
             if (opt != 0)
@@ -603,9 +589,6 @@
             if (props.getBoolean(Utils.COM_PREFIX+"strip.exceptions"))   pkg.stripAttributeKind("Exceptions");
             if (props.getBoolean(Utils.COM_PREFIX+"strip.innerclasses")) pkg.stripAttributeKind("InnerClasses");
 
-            // Must choose an archive version; PackageWriter does not.
-            if (pkg.package_majver <= 0)  pkg.choosePackageVersion();
-
             PackageWriter pw = new PackageWriter(pkg, out);
             pw.archiveNextCount = nextCount;
             pw.write();
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/PropMap.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/PropMap.java	Tue May 29 14:56:48 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -193,13 +193,18 @@
     boolean setBoolean(String s, boolean val) {
         return toBoolean(setProperty(s, String.valueOf(val)));
     }
-
     int toInteger(String val) {
-        if (val == null)  return 0;
+        return toInteger(val, 0);
+    }
+    int toInteger(String val, int def) {
+        if (val == null)  return def;
         if (Pack200.Packer.TRUE.equals(val))   return 1;
         if (Pack200.Packer.FALSE.equals(val))  return 0;
         return Integer.parseInt(val);
     }
+    int getInteger(String s, int def) {
+        return toInteger(getProperty(s), def);
+    }
     int getInteger(String s) {
         return toInteger(getProperty(s));
     }
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Utils.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Utils.java	Tue May 29 14:56:48 2012 -0700
@@ -25,12 +25,6 @@
 
 package com.sun.java.util.jar.pack;
 
-import com.sun.java.util.jar.pack.ConstantPool.ClassEntry;
-import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry;
-import com.sun.java.util.jar.pack.ConstantPool.LiteralEntry;
-import com.sun.java.util.jar.pack.ConstantPool.MemberEntry;
-import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry;
-import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -38,9 +32,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Collections;
 import java.util.Date;
-import java.util.Enumeration;
-import java.util.Map;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.jar.JarInputStream;
@@ -211,9 +204,7 @@
     static String getVersionString() {
         return "Pack200, Vendor: " +
             System.getProperty("java.vendor") +
-            ", Version: " +
-            Constants.JAVA6_PACKAGE_MAJOR_VERSION + "." +
-            Constants.JAVA6_PACKAGE_MINOR_VERSION;
+            ", Version: " + Constants.MAX_PACKAGE_VERSION;
     }
 
     static void markJarFile(JarOutputStream out) throws IOException {
@@ -240,8 +231,7 @@
     }
     static void copyJarFile(JarFile in, JarOutputStream out) throws IOException {
         byte[] buffer = new byte[1 << 14];
-        for (Enumeration<JarEntry> e = in.entries(); e.hasMoreElements(); ) {
-            JarEntry je = e.nextElement();
+        for (JarEntry je : Collections.list(in.entries())) {
             out.putNextEntry(je);
             InputStream ein = in.getInputStream(je);
             for (int nr; 0 < (nr = ein.read(buffer)); ) {
--- a/jdk/test/tools/pack200/PackageVersionTest.java	Tue May 29 13:16:17 2012 -0700
+++ b/jdk/test/tools/pack200/PackageVersionTest.java	Tue May 29 14:56:48 2012 -0700
@@ -24,7 +24,7 @@
 
 /*
  * @test
- * @bug 6712743 6991164
+ * @bug 6712743 6991164 7168401
  * @summary verify package versions
  * @compile -XDignore.symbol.file Utils.java PackageVersionTest.java
  * @run main PackageVersionTest
@@ -71,8 +71,9 @@
         verifyPack("Test6.class", JAVA6_PACKAGE_MAJOR_VERSION,
                 JAVA6_PACKAGE_MINOR_VERSION);
 
-        verifyPack("Test7.class", JAVA7_PACKAGE_MAJOR_VERSION,
-                JAVA7_PACKAGE_MINOR_VERSION);
+        // a jar file devoid of indy classes must generate 160.1 package file
+        verifyPack("Test7.class", JAVA6_PACKAGE_MAJOR_VERSION,
+                JAVA6_PACKAGE_MINOR_VERSION);
 
         // test for resource file, ie. no class files
         verifyPack("Test6.java", JAVA5_PACKAGE_MAJOR_VERSION,
@@ -84,7 +85,7 @@
         String versionStr = unpacker.toString();
         String expected = "Pack200, Vendor: " +
                 System.getProperty("java.vendor") + ", Version: " +
-                JAVA6_PACKAGE_MAJOR_VERSION + "." + JAVA6_PACKAGE_MINOR_VERSION;
+                JAVA7_PACKAGE_MAJOR_VERSION + "." + JAVA7_PACKAGE_MINOR_VERSION;
         if (!versionStr.equals(expected)) {
             System.out.println("Expected: " + expected);
             System.out.println("Obtained: " + versionStr);