Merge
authoramurillo
Thu, 09 Jul 2015 22:46:18 -0700
changeset 31678 7a5da9274448
parent 31564 9b3a9d72f07b (current diff)
parent 31677 23b656089d20 (diff)
child 31679 68ec1eea9328
child 31809 642909796280
child 31810 3b32c5ff56e4
Merge
jdk/src/java.base/share/classes/java/util/Arrays.java
jdk/src/java.base/share/classes/jdk/internal/jimage/ImageFile.java
jdk/src/java.base/share/classes/jdk/internal/jimage/ImageModules.java
jdk/src/java.base/share/classes/jdk/internal/jimage/PReader.java
jdk/src/java.base/share/classes/jdk/internal/jimage/PackageModuleMap.java
jdk/src/java.base/share/classes/jdk/internal/jimage/Resource.java
jdk/src/java.base/share/classes/jdk/internal/jimage/concurrent/ConcurrentPReader.java
jdk/src/java.base/unix/native/libjava/ConcurrentPReader_md.c
jdk/src/java.base/windows/native/libjava/ConcurrentPReader_md.c
--- a/jdk/make/Tools.gmk	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/make/Tools.gmk	Thu Jul 09 22:46:18 2015 -0700
@@ -144,7 +144,6 @@
     SETUP := GENERATE_OLDBYTECODE, \
     SRC := $(JDK_TOPDIR)/src/java.base/share/classes, \
     INCLUDES := $(JIMAGE_PKGS), \
-    EXCLUDES := jdk/internal/jimage/concurrent, \
     BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_jimage_classes))
 
 # Because of the explicit INCLUDES in the compilation setup above, the service provider
--- a/jdk/make/mapfiles/libjava/mapfile-vers	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/make/mapfiles/libjava/mapfile-vers	Thu Jul 09 22:46:18 2015 -0700
@@ -239,6 +239,16 @@
 		Java_java_util_TimeZone_getSystemTimeZoneID;
 		Java_java_util_TimeZone_getSystemGMTOffsetID;
 		Java_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8;
+                Java_jdk_internal_jimage_ImageNativeSubstrate_openImage;
+                Java_jdk_internal_jimage_ImageNativeSubstrate_closeImage;
+                Java_jdk_internal_jimage_ImageNativeSubstrate_getIndexAddress;
+                Java_jdk_internal_jimage_ImageNativeSubstrate_getDataAddress;
+                Java_jdk_internal_jimage_ImageNativeSubstrate_read;
+                Java_jdk_internal_jimage_ImageNativeSubstrate_readCompressed;
+                Java_jdk_internal_jimage_ImageNativeSubstrate_getStringBytes;
+                Java_jdk_internal_jimage_ImageNativeSubstrate_getAttributes;
+                Java_jdk_internal_jimage_ImageNativeSubstrate_findAttributes;
+                Java_jdk_internal_jimage_ImageNativeSubstrate_attributeOffsets;
 		Java_sun_misc_MessageUtils_toStderr;
 		Java_sun_misc_MessageUtils_toStdout;
 		Java_sun_misc_NativeSignalHandler_handle0;
@@ -281,9 +291,6 @@
 		Java_sun_misc_VMSupport_initAgentProperties;
 		Java_sun_misc_VMSupport_getVMTemporaryDirectory;
 
-                Java_jdk_internal_jimage_concurrent_ConcurrentPReader_initIDs;
-                Java_jdk_internal_jimage_concurrent_ConcurrentPReader_pread;
-
                 # ZipFile.c needs this one
 		throwFileNotFoundException;
                 # zip_util.c needs this one
--- a/jdk/make/mapfiles/libzip/mapfile-vers	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/make/mapfiles/libzip/mapfile-vers	Thu Jul 09 22:46:18 2015 -0700
@@ -32,8 +32,8 @@
 		Java_java_util_zip_Adler32_updateBytes;
 		Java_java_util_zip_Adler32_updateByteBuffer;
 		Java_java_util_zip_CRC32_update;
-		Java_java_util_zip_CRC32_updateBytes;
-		Java_java_util_zip_CRC32_updateByteBuffer;
+		Java_java_util_zip_CRC32_updateBytes0;
+		Java_java_util_zip_CRC32_updateByteBuffer0;
 		Java_java_util_zip_Deflater_deflateBytes;
 		Java_java_util_zip_Deflater_end;
 		Java_java_util_zip_Deflater_getAdler;
--- a/jdk/make/src/classes/build/tools/module/ImageBuilder.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/make/src/classes/build/tools/module/ImageBuilder.java	Thu Jul 09 22:46:18 2015 -0700
@@ -26,8 +26,6 @@
 package build.tools.module;
 
 import jdk.internal.jimage.Archive;
-import jdk.internal.jimage.ImageFile;
-import jdk.internal.jimage.ImageModules;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -35,13 +33,11 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
-import java.io.UncheckedIOException;
 import java.nio.ByteOrder;
 import java.nio.file.Files;
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.nio.file.attribute.PosixFilePermission;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -52,6 +48,7 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
+import jdk.internal.jimage.ImageFileCreator;
 
 /**
  * A tool for building a runtime image.
@@ -84,11 +81,23 @@
         boolean showUsage;
     }
 
-    static abstract class Option {
+    static class Option {
+
+        interface Processing {
+
+            void process(ImageBuilder task, String opt, String arg) throws BadArgs;
+        }
+
         final boolean hasArg;
         final String[] aliases;
-        Option(boolean hasArg, String... aliases) {
+        final String description;
+        final Processing processing;
+
+        Option(boolean hasArg, String description, Processing processing,
+                String... aliases) {
             this.hasArg = hasArg;
+            this.description = description;
+            this.processing = processing;
             this.aliases = aliases;
         }
         boolean isHidden() {
@@ -107,8 +116,12 @@
         boolean ignoreRest() {
             return false;
         }
-        abstract void process(ImageBuilder task, String opt, String arg) throws BadArgs;
-        abstract String description();
+        void process(ImageBuilder task, String opt, String arg) throws BadArgs {
+            processing.process(task, opt, arg);
+        }
+        String description() {
+            return description;
+        }
     }
 
     private static Path CWD = Paths.get("");
@@ -133,64 +146,44 @@
     }
 
     static Option[] recognizedOptions = {
-        new Option(true, "--cmds") {
-            void process(ImageBuilder task, String opt, String arg) throws BadArgs {
-                task.options.cmds = splitPath(arg, File.pathSeparator);
-            }
-            String description() { return "Location of native commands"; }
-        },
-        new Option(true, "--configs") {
-            void process(ImageBuilder task, String opt, String arg) throws BadArgs {
-                task.options.configs = splitPath(arg, File.pathSeparator);
-            }
-            String description() { return "Location of config files"; }
-        },
-        new Option(false, "--help") {
-            void process(ImageBuilder task, String opt, String arg) {
-                task.options.help = true;
-            }
-            String description() { return "Print this usage message"; }
-        },
-        new Option(true, "--classes") {
-            void process(ImageBuilder task, String opt, String arg) throws BadArgs {
-                task.options.classes = splitPath(arg, File.pathSeparator);
-            }
-            String description() { return "Location of module classes files"; }
-        },
-        new Option(true, "--libs") {
-            void process(ImageBuilder task, String opt, String arg) throws BadArgs {
-                task.options.libs = splitPath(arg, File.pathSeparator);
+        new Option(true, "Location of native commands", (task, opt, arg) -> {
+            task.options.cmds = splitPath(arg, File.pathSeparator);
+        }, "--cmds"),
+        new Option(true, "Location of config files", (task, opt, arg) -> {
+            task.options.configs = splitPath(arg, File.pathSeparator);
+        }, "--configs"),
+        new Option(false, "Print this usage message", (task, opt, arg) -> {
+            task.options.help = true;
+        }, "--help"),
+        new Option(true, "Location of module classes files", (task, opt, arg) -> {
+            task.options.classes = splitPath(arg, File.pathSeparator);
+        }, "--classes"),
+        new Option(true, "Location of native libraries", (task, opt, arg) -> {
+            task.options.libs = splitPath(arg, File.pathSeparator);
+        }, "--libs"),
+        new Option(true, "Comma separated list of module names",
+        (task, opt, arg) -> {
+            for (String mn : arg.split(",")) {
+                if (mn.isEmpty()) {
+                    throw new BadArgs("Module not found", mn);
+                }
+                task.options.mods.add(mn);
             }
-            String description() { return "Location of native libraries"; }
-        },
-        new Option(true, "--mods") {
-            void process(ImageBuilder task, String opt, String arg) throws BadArgs {
-                for (String mn : arg.split(",")) {
-                    if (mn.isEmpty())
-                        throw new BadArgs("Module not found", mn);
-                    task.options.mods.add(mn);
-                }
+        }, "--mods"),
+        new Option(true, "Location of the output path", (task, opt, arg) -> {
+            Path path = Paths.get(arg);
+            task.options.output = path;
+        }, "--output"),
+        new Option(true, "Byte order of the target runtime; {little,big}",
+        (task, opt, arg) -> {
+            if (arg.equals("little")) {
+                task.options.endian = ByteOrder.LITTLE_ENDIAN;
+            } else if (arg.equals("big")) {
+                task.options.endian = ByteOrder.BIG_ENDIAN;
+            } else {
+                throw new BadArgs("Unknown byte order " + arg);
             }
-            String description() { return "Comma separated list of module names"; }
-        },
-        new Option(true, "--output") {
-            void process(ImageBuilder task, String opt, String arg) throws BadArgs {
-                Path path = Paths.get(arg);
-                task.options.output = path;
-            }
-            String description() { return "Location of the output path"; }
-        },
-        new Option(true, "--endian") {
-            void process(ImageBuilder task, String opt, String arg) throws BadArgs {
-                if (arg.equals("little"))
-                    task.options.endian = ByteOrder.LITTLE_ENDIAN;
-                else if (arg.equals("big"))
-                    task.options.endian = ByteOrder.BIG_ENDIAN;
-                else
-                    throw new BadArgs("Unknown byte order " + arg);
-            }
-            String description() { return "Byte order of the target runtime; {little,big}"; }
-        }
+        }, "--endian")
     };
 
     private final Options options = new Options();
@@ -370,28 +363,35 @@
         final Set<String> bootModules;
         final Set<String> extModules;
         final Set<String> appModules;
-        final ImageModules imf;
 
         ImageFileHelper(Collection<String> modules) throws IOException {
             this.modules = modules;
             this.bootModules = modulesFor(BOOT_MODULES).stream()
-                     .filter(modules::contains)
-                     .collect(Collectors.toSet());
+                    .filter(modules::contains)
+                    .collect(Collectors.toSet());
             this.extModules = modulesFor(EXT_MODULES).stream()
                     .filter(modules::contains)
                     .collect(Collectors.toSet());
             this.appModules = modules.stream()
-                    .filter(m -> !bootModules.contains(m) && !extModules.contains(m))
+                    .filter(m -> m.length() != 0 &&
+                                 !bootModules.contains(m) &&
+                                 !extModules.contains(m))
                     .collect(Collectors.toSet());
-
-            this.imf = new ImageModules(bootModules, extModules, appModules);
         }
 
         void createModularImage(Path output) throws IOException {
-            Set<Archive> archives = modules.stream()
-                                            .map(this::toModuleArchive)
-                                            .collect(Collectors.toSet());
-            ImageFile.create(output, archives, imf, options.endian);
+            Set<Archive> bootArchives = bootModules.stream()
+                    .map(this::toModuleArchive)
+                    .collect(Collectors.toSet());
+            Set<Archive> extArchives = extModules.stream()
+                    .map(this::toModuleArchive)
+                    .collect(Collectors.toSet());
+            Set<Archive> appArchives = appModules.stream()
+                    .map(this::toModuleArchive)
+                    .collect(Collectors.toSet());
+            ImageFileCreator.create(output, "bootmodules", bootArchives, options.endian);
+            ImageFileCreator.create(output, "extmodules", extArchives, options.endian);
+            ImageFileCreator.create(output, "appmodules", appArchives, options.endian);
         }
 
         ModuleArchive toModuleArchive(String mn) {
--- a/jdk/make/src/classes/build/tools/module/ModuleArchive.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/make/src/classes/build/tools/module/ModuleArchive.java	Thu Jul 09 22:46:18 2015 -0700
@@ -26,15 +26,17 @@
 package build.tools.module;
 
 import jdk.internal.jimage.Archive;
-import jdk.internal.jimage.Resource;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.UncheckedIOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.function.Consumer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import jdk.internal.jimage.Archive.Entry.EntryType;
 
 /**
  * An Archive backed by an exploded representation on disk.
@@ -46,6 +48,8 @@
     private final Path configs;
     private final String moduleName;
 
+    private final List<InputStream> opened = new ArrayList<>();
+
     public ModuleArchive(String moduleName, Path classes, Path cmds,
                          Path libs, Path configs) {
         this.moduleName = moduleName;
@@ -61,183 +65,119 @@
     }
 
     @Override
-    public void visitResources(Consumer<Resource> consumer) {
-        if (classes == null)
-            return;
-        try{
-            Files.walk(classes)
-                    .sorted()
-                    .filter(p -> !Files.isDirectory(p)
-                            && !classes.relativize(p).toString().startsWith("_the.")
-                            && !classes.relativize(p).toString().equals("javac_state"))
-                    .map(this::toResource)
-                    .forEach(consumer::accept);
-        } catch (IOException ioe) {
-            throw new UncheckedIOException(ioe);
-        }
+    public void open() throws IOException {
+        // NOOP
     }
 
-    private Resource toResource(Path path) {
-        try {
-            return new Resource(classes.relativize(path).toString().replace('\\','/'),
-                                Files.size(path),
-                                0 /* no compression support yet */);
-        } catch (IOException ioe) {
-            throw new UncheckedIOException(ioe);
+    @Override
+    public void close() throws IOException {
+        IOException e = null;
+        for (InputStream stream : opened) {
+            try {
+                stream.close();
+            } catch (IOException ex) {
+                if (e == null) {
+                    e = ex;
+                } else {
+                    e.addSuppressed(ex);
+                }
+            }
         }
-    }
-
-    private enum Section {
-        CLASSES,
-        CMDS,
-        LIBS,
-        CONFIGS
+        if (e != null) {
+            throw e;
+        }
     }
 
     @Override
-    public void visitEntries(Consumer<Entry> consumer) {
-        try{
-            if (classes != null)
-                Files.walk(classes)
-                        .sorted()
-                        .filter(p -> !Files.isDirectory(p)
-                                && !classes.relativize(p).toString().startsWith("_the.")
-                                && !classes.relativize(p).toString().equals("javac_state"))
-                        .map(p -> toEntry(p, classes, Section.CLASSES))
-                        .forEach(consumer::accept);
-            if (cmds != null)
-                Files.walk(cmds)
+    public Stream<Entry> entries() {
+        List<Entry> entries = new ArrayList<>();
+        try {
+            /*
+             * This code should be revisited to avoid buffering of the entries.
+             * 1) Do we really need sorting classes? This force buffering of entries.
+             *    libs, cmds and configs are not sorted.
+             * 2) I/O streams should be concatenated instead of buffering into
+             *    entries list.
+             * 3) Close I/O streams in a close handler.
+             */
+            if (classes != null) {
+                try (Stream<Path> stream = Files.walk(classes)) {
+                    entries.addAll(stream
+                            .filter(p -> !Files.isDirectory(p)
+                                    && !classes.relativize(p).toString().startsWith("_the.")
+                                    && !classes.relativize(p).toString().equals("javac_state"))
+                            .sorted()
+                            .map(p -> toEntry(p, classes, EntryType.CLASS_OR_RESOURCE))
+                            .collect(Collectors.toList()));
+                }
+            }
+            if (cmds != null) {
+                try (Stream<Path> stream = Files.walk(cmds)) {
+                    entries.addAll(stream
+                            .filter(p -> !Files.isDirectory(p))
+                            .map(p -> toEntry(p, cmds, EntryType.NATIVE_CMD))
+                            .collect(Collectors.toList()));
+                }
+            }
+            if (libs != null) {
+                try (Stream<Path> stream = Files.walk(libs)) {
+                    entries.addAll(stream
+                            .filter(p -> !Files.isDirectory(p))
+                            .map(p -> toEntry(p, libs, EntryType.NATIVE_LIB))
+                            .collect(Collectors.toList()));
+                }
+            }
+            if (configs != null) {
+                try (Stream<Path> stream = Files.walk(configs)) {
+                entries.addAll(stream
                         .filter(p -> !Files.isDirectory(p))
-                        .map(p -> toEntry(p, cmds, Section.CMDS))
-                        .forEach(consumer::accept);
-            if (libs != null)
-                Files.walk(libs)
-                        .filter(p -> !Files.isDirectory(p))
-                        .map(p -> toEntry(p, libs, Section.LIBS))
-                        .forEach(consumer::accept);
-            if (configs != null)
-                Files.walk(configs)
-                        .filter(p -> !Files.isDirectory(p))
-                        .map(p -> toEntry(p, configs, Section.CONFIGS))
-                        .forEach(consumer::accept);
+                        .map(p -> toEntry(p, configs, EntryType.CONFIG))
+                        .collect(Collectors.toList()));
+                }
+            }
         } catch (IOException ioe) {
             throw new UncheckedIOException(ioe);
         }
+        return entries.stream();
+    }
+
+    private class FileEntry extends Entry {
+        private final boolean isDirectory;
+        private final long size;
+        private final Path entryPath;
+        FileEntry(Path entryPath, String path, EntryType type,
+                  boolean isDirectory, long size) {
+            super(ModuleArchive.this, path, path, type);
+            this.entryPath = entryPath;
+            this.isDirectory = isDirectory;
+            this.size = size;
+        }
+
+        public boolean isDirectory() {
+            return isDirectory;
+        }
+
+        @Override
+        public long size() {
+            return size;
+        }
+
+        @Override
+        public InputStream stream() throws IOException {
+            InputStream stream = Files.newInputStream(entryPath);
+            opened.add(stream);
+            return stream;
+        }
     }
 
-    private static class FileEntry implements Entry {
-        private final String name;
-        private final InputStream is;
-        private final boolean isDirectory;
-        private final Section section;
-        FileEntry(String name, InputStream is,
-                  boolean isDirectory, Section section) {
-            this.name = name;
-            this.is = is;
-            this.isDirectory = isDirectory;
-            this.section = section;
-        }
-        public String getName() {
-            return name;
-        }
-        public Section getSection() {
-            return section;
-        }
-        public InputStream getInputStream() {
-            return is;
-        }
-        public boolean isDirectory() {
-            return isDirectory;
-        }
-    }
-
-    private Entry toEntry(Path entryPath, Path basePath, Section section) {
+    private Entry toEntry(Path entryPath, Path basePath, EntryType section) {
         try {
-            return new FileEntry(basePath.relativize(entryPath).toString().replace('\\', '/'),
-                                 Files.newInputStream(entryPath), false,
-                                 section);
+            String path = basePath.relativize(entryPath).toString().replace('\\', '/');
+            return new FileEntry(entryPath, path, section,
+                    false, Files.size(entryPath));
         } catch (IOException e) {
             throw new UncheckedIOException(e);
         }
     }
-
-    @Override
-    public Consumer<Entry> defaultImageWriter(Path path, OutputStream out) {
-        return new DefaultEntryWriter(path, out);
-    }
-
-    private static class DefaultEntryWriter implements Consumer<Archive.Entry> {
-        private final Path root;
-        private final OutputStream out;
-
-        DefaultEntryWriter(Path root, OutputStream out) {
-            this.root = root;
-            this.out = out;
-        }
-
-        @Override
-        public void accept(Archive.Entry entry) {
-            try {
-                FileEntry e = (FileEntry)entry;
-                Section section = e.getSection();
-                String filename = e.getName();
-
-                try (InputStream in = entry.getInputStream()) {
-                    switch (section) {
-                        case CLASSES:
-                            if (!filename.startsWith("_the.") && !filename.equals("javac_state"))
-                                writeEntry(in);
-                            break;
-                        case LIBS:
-                            writeEntry(in, destFile(nativeDir(filename), filename));
-                            break;
-                        case CMDS:
-                            Path path = destFile("bin", filename);
-                            writeEntry(in, path);
-                            path.toFile().setExecutable(true, false);
-                            break;
-                        case CONFIGS:
-                            writeEntry(in, destFile("conf", filename));
-                            break;
-                        default:
-                            throw new InternalError("unexpected entry: " + filename);
-                    }
-                }
-            } catch (IOException x) {
-                throw new UncheckedIOException(x);
-            }
-        }
-
-        private Path destFile(String dir, String filename) {
-            return root.resolve(dir).resolve(filename);
-        }
-
-        private static void writeEntry(InputStream in, Path dstFile) throws IOException {
-            if (Files.notExists(dstFile.getParent()))
-                Files.createDirectories(dstFile.getParent());
-            Files.copy(in, dstFile);
-        }
-
-        private void writeEntry(InputStream in) throws IOException {
-            byte[] buf = new byte[8192];
-            int n;
-            while ((n = in.read(buf)) > 0)
-                out.write(buf, 0, n);
-        }
-
-        private static String nativeDir(String filename) {
-            if (System.getProperty("os.name").startsWith("Windows")) {
-                if (filename.endsWith(".dll") || filename.endsWith(".diz")
-                    || filename.endsWith(".pdb") || filename.endsWith(".map")
-                    || filename.endsWith(".cpl")) {
-                    return "bin";
-                } else {
-                    return "lib";
-                }
-            } else {
-                return "lib";
-            }
-        }
-    }
 }
 
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCrypt.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCrypt.java	Thu Jul 09 22:46:18 2015 -0700
@@ -38,6 +38,9 @@
 
 import java.security.InvalidKeyException;
 import java.util.Arrays;
+import java.util.Objects;
+
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * Rijndael --pronounced Reindaal-- is a symmetric cipher with a 128-bit
@@ -346,7 +349,16 @@
      * Encrypt exactly one block of plaintext.
      */
     void encryptBlock(byte[] in, int inOffset,
-                              byte[] out, int outOffset)
+                      byte[] out, int outOffset) {
+        cryptBlockCheck(in, inOffset);
+        cryptBlockCheck(out, outOffset);
+        implEncryptBlock(in, inOffset, out, outOffset);
+    }
+
+    // Encryption operation. Possibly replaced with a compiler intrinsic.
+    @HotSpotIntrinsicCandidate
+    private void implEncryptBlock(byte[] in, int inOffset,
+                                  byte[] out, int outOffset)
     {
         int keyOffset = 0;
         int t0   = ((in[inOffset++]       ) << 24 |
@@ -412,12 +424,20 @@
         out[outOffset  ] = (byte)(S[(t2       ) & 0xFF] ^ (tt       ));
     }
 
-
     /**
      * Decrypt exactly one block of plaintext.
      */
     void decryptBlock(byte[] in, int inOffset,
-                              byte[] out, int outOffset)
+                      byte[] out, int outOffset) {
+        cryptBlockCheck(in, inOffset);
+        cryptBlockCheck(out, outOffset);
+        implDecryptBlock(in, inOffset, out, outOffset);
+    }
+
+    // Decrypt operation. Possibly replaced with a compiler intrinsic.
+    @HotSpotIntrinsicCandidate
+    private void implDecryptBlock(byte[] in, int inOffset,
+                                  byte[] out, int outOffset)
     {
         int keyOffset = 4;
         int t0 = ((in[inOffset++]       ) << 24 |
@@ -572,6 +592,25 @@
         out[outOffset  ] = (byte)(Si[(a0       ) & 0xFF] ^ (t1       ));
     }
 
+    // Used to perform all checks required by the Java semantics
+    // (i.e., null checks and bounds checks) on the input parameters
+    // to encryptBlock and to decryptBlock.
+    // Normally, the Java Runtime performs these checks, however, as
+    // encryptBlock and decryptBlock are possibly replaced with
+    // compiler intrinsics, the JDK performs the required checks instead.
+    // Does not check accesses to class-internal (private) arrays.
+    private static void cryptBlockCheck(byte[] array, int offset) {
+        Objects.requireNonNull(array);
+
+        if (offset < 0 || offset >= array.length) {
+            throw new ArrayIndexOutOfBoundsException(offset);
+        }
+
+        int largestIndex = offset + AES_BLOCK_SIZE - 1;
+        if (largestIndex < 0 || largestIndex >= array.length) {
+            throw new ArrayIndexOutOfBoundsException(largestIndex);
+        }
+    }
 
     /**
      * Expand a user-supplied key material into a session key.
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherBlockChaining.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherBlockChaining.java	Thu Jul 09 22:46:18 2015 -0700
@@ -27,6 +27,9 @@
 
 import java.security.InvalidKeyException;
 import java.security.ProviderException;
+import java.util.Objects;
+
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 
 /**
@@ -138,15 +141,22 @@
      * @return the length of the encrypted data
      */
     int encrypt(byte[] plain, int plainOffset, int plainLen,
-                byte[] cipher, int cipherOffset)
+                byte[] cipher, int cipherOffset) {
+        cryptBlockSizeCheck(plainLen);
+        cryptNullAndBoundsCheck(plain, plainOffset, plainLen);
+        cryptNullAndBoundsCheck(cipher, cipherOffset, plainLen);
+        return implEncrypt(plain, plainOffset, plainLen,
+                           cipher, cipherOffset);
+    }
+
+    @HotSpotIntrinsicCandidate
+    private int implEncrypt(byte[] plain, int plainOffset, int plainLen,
+                            byte[] cipher, int cipherOffset)
     {
-        if ((plainLen % blockSize) != 0) {
-            throw new ProviderException("Internal error in input buffering");
-        }
         int endIndex = plainOffset + plainLen;
 
         for (; plainOffset < endIndex;
-             plainOffset+=blockSize, cipherOffset += blockSize) {
+             plainOffset += blockSize, cipherOffset += blockSize) {
             for (int i = 0; i < blockSize; i++) {
                 k[i] = (byte)(plain[i + plainOffset] ^ r[i]);
             }
@@ -179,11 +189,17 @@
      * @return the length of the decrypted data
      */
     int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
-                byte[] plain, int plainOffset)
+                byte[] plain, int plainOffset) {
+        cryptBlockSizeCheck(cipherLen);
+        cryptNullAndBoundsCheck(cipher, cipherOffset, cipherLen);
+        cryptNullAndBoundsCheck(plain, plainOffset, cipherLen);
+        return implDecrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
+    }
+
+    @HotSpotIntrinsicCandidate
+    private int implDecrypt(byte[] cipher, int cipherOffset, int cipherLen,
+                            byte[] plain, int plainOffset)
     {
-        if ((cipherLen % blockSize) != 0) {
-            throw new ProviderException("Internal error in input buffering");
-        }
         int endIndex = cipherOffset + cipherLen;
 
         for (; cipherOffset < endIndex;
@@ -196,4 +212,27 @@
         }
         return cipherLen;
     }
+
+    private void cryptBlockSizeCheck(int len) {
+        if ((len % blockSize) != 0) {
+            throw new ProviderException("Internal error in input buffering");
+        }
+    }
+
+    private static void cryptNullAndBoundsCheck(byte[] array, int offset, int len) {
+        if (len <= 0) {
+            return; // not an error because cryptImpl/decryptImpl won't execute if len <= 0
+        }
+
+        Objects.requireNonNull(array);
+
+        if (offset < 0 || offset >= array.length) {
+            throw new ArrayIndexOutOfBoundsException(offset);
+        }
+
+        int endIndex = offset + len - 1;
+        if (endIndex < 0 || endIndex >= array.length) {
+            throw new ArrayIndexOutOfBoundsException(endIndex);
+        }
+    }
 }
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/GHASH.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/GHASH.java	Thu Jul 09 22:46:18 2015 -0700
@@ -31,6 +31,8 @@
 
 import java.security.ProviderException;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
+
 /**
  * This class represents the GHASH function defined in NIST 800-38D
  * under section 6.4. It needs to be constructed w/ a hash subkey, i.e.
@@ -227,6 +229,7 @@
      * the hotspot signature.  This method and methods called by it, cannot
      * throw exceptions or allocate arrays as it will breaking intrinsics
      */
+    @HotSpotIntrinsicCandidate
     private static void processBlocks(byte[] data, int inOfs, int blocks, long[] st, long[] subH) {
         int offset = inOfs;
         while (blocks > 0) {
--- a/jdk/src/java.base/share/classes/java/lang/Boolean.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Boolean.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,6 +25,8 @@
 
 package java.lang;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
+
 /**
  * The Boolean class wraps a value of the primitive type
  * {@code boolean} in an object. An object of type
@@ -128,6 +130,7 @@
      *
      * @return  the primitive {@code boolean} value of this object.
      */
+    @HotSpotIntrinsicCandidate
     public boolean booleanValue() {
         return value;
     }
@@ -146,6 +149,7 @@
      * @return a {@code Boolean} instance representing {@code b}.
      * @since  1.4
      */
+    @HotSpotIntrinsicCandidate
     public static Boolean valueOf(boolean b) {
         return (b ? TRUE : FALSE);
     }
--- a/jdk/src/java.base/share/classes/java/lang/Byte.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Byte.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,6 +25,8 @@
 
 package java.lang;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
+
 /**
  *
  * The {@code Byte} class wraps a value of primitive type {@code byte}
@@ -98,6 +100,7 @@
      * @return a {@code Byte} instance representing {@code b}.
      * @since  1.5
      */
+    @HotSpotIntrinsicCandidate
     public static Byte valueOf(byte b) {
         final int offset = 128;
         return ByteCache.cache[(int)b + offset];
@@ -320,6 +323,7 @@
      * Returns the value of this {@code Byte} as a
      * {@code byte}.
      */
+    @HotSpotIntrinsicCandidate
     public byte byteValue() {
         return value;
     }
--- a/jdk/src/java.base/share/classes/java/lang/Character.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Character.java	Thu Jul 09 22:46:18 2015 -0700
@@ -30,6 +30,8 @@
 import java.util.HashMap;
 import java.util.Locale;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
+
 /**
  * The {@code Character} class wraps a value of the primitive
  * type {@code char} in an object. An object of type
@@ -4569,6 +4571,7 @@
      * @return a <tt>Character</tt> instance representing <tt>c</tt>.
      * @since  1.5
      */
+    @HotSpotIntrinsicCandidate
     public static Character valueOf(char c) {
         if (c <= 127) { // must cache
             return CharacterCache.cache[(int)c];
@@ -4581,6 +4584,7 @@
      * @return  the primitive {@code char} value represented by
      *          this object.
      */
+    @HotSpotIntrinsicCandidate
     public char charValue() {
         return value;
     }
@@ -7181,6 +7185,7 @@
      *     the bytes in the specified <tt>char</tt> value.
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static char reverseBytes(char ch) {
         return (char) (((ch & 0xFF00) >> 8) | (ch << 8));
     }
--- a/jdk/src/java.base/share/classes/java/lang/Class.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Class.java	Thu Jul 09 22:46:18 2015 -0700
@@ -56,6 +56,7 @@
 import java.util.Objects;
 import java.util.StringJoiner;
 import sun.misc.Unsafe;
+import jdk.internal.HotSpotIntrinsicCandidate;
 import sun.reflect.CallerSensitive;
 import sun.reflect.ConstantPool;
 import sun.reflect.Reflection;
@@ -502,6 +503,7 @@
      *
      * @since 1.1
      */
+    @HotSpotIntrinsicCandidate
     public native boolean isInstance(Object obj);
 
 
@@ -529,6 +531,7 @@
      *            null.
      * @since 1.1
      */
+    @HotSpotIntrinsicCandidate
     public native boolean isAssignableFrom(Class<?> cls);
 
 
@@ -539,6 +542,7 @@
      * @return  {@code true} if this object represents an interface;
      *          {@code false} otherwise.
      */
+    @HotSpotIntrinsicCandidate
     public native boolean isInterface();
 
 
@@ -549,6 +553,7 @@
      *          {@code false} otherwise.
      * @since   1.1
      */
+    @HotSpotIntrinsicCandidate
     public native boolean isArray();
 
 
@@ -580,6 +585,7 @@
      * @see     java.lang.Void#TYPE
      * @since 1.1
      */
+    @HotSpotIntrinsicCandidate
     public native boolean isPrimitive();
 
     /**
@@ -751,6 +757,7 @@
      *
      * @return the direct superclass of the class represented by this object
      */
+    @HotSpotIntrinsicCandidate
     public native Class<? super T> getSuperclass();
 
 
@@ -984,6 +991,7 @@
      * @see     java.lang.reflect.Modifier
      * @since 1.1
      */
+    @HotSpotIntrinsicCandidate
     public native int getModifiers();
 
 
@@ -3382,6 +3390,7 @@
      * @since 1.5
      */
     @SuppressWarnings("unchecked")
+    @HotSpotIntrinsicCandidate
     public T cast(Object obj) {
         if (obj != null && !isInstance(obj))
             throw new ClassCastException(cannotCastMsg(obj));
--- a/jdk/src/java.base/share/classes/java/lang/Double.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Double.java	Thu Jul 09 22:46:18 2015 -0700
@@ -27,6 +27,7 @@
 
 import sun.misc.FloatingDecimal;
 import sun.misc.DoubleConsts;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * The {@code Double} class wraps a value of the primitive type
@@ -514,6 +515,7 @@
      * @return a {@code Double} instance representing {@code d}.
      * @since  1.5
      */
+    @HotSpotIntrinsicCandidate
     public static Double valueOf(double d) {
         return new Double(d);
     }
@@ -711,6 +713,7 @@
      *
      * @return the {@code double} value represented by this object
      */
+    @HotSpotIntrinsicCandidate
     public double doubleValue() {
         return value;
     }
@@ -831,6 +834,7 @@
      * @param   value   a {@code double} precision floating-point number.
      * @return the bits that represent the floating-point number.
      */
+    @HotSpotIntrinsicCandidate
     public static long doubleToLongBits(double value) {
         if (!isNaN(value)) {
             return doubleToRawLongBits(value);
@@ -874,6 +878,7 @@
      * @return the bits that represent the floating-point number.
      * @since 1.3
      */
+    @HotSpotIntrinsicCandidate
     public static native long doubleToRawLongBits(double value);
 
     /**
@@ -937,6 +942,7 @@
      * @return  the {@code double} floating-point value with the same
      *          bit pattern.
      */
+    @HotSpotIntrinsicCandidate
     public static native double longBitsToDouble(long bits);
 
     /**
--- a/jdk/src/java.base/share/classes/java/lang/Float.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Float.java	Thu Jul 09 22:46:18 2015 -0700
@@ -28,6 +28,7 @@
 import sun.misc.FloatingDecimal;
 import sun.misc.FloatConsts;
 import sun.misc.DoubleConsts;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * The {@code Float} class wraps a value of primitive type
@@ -429,6 +430,7 @@
      * @return a {@code Float} instance representing {@code f}.
      * @since  1.5
      */
+    @HotSpotIntrinsicCandidate
     public static Float valueOf(float f) {
         return new Float(f);
     }
@@ -622,6 +624,7 @@
      *
      * @return the {@code float} value represented by this object
      */
+    @HotSpotIntrinsicCandidate
     public float floatValue() {
         return value;
     }
@@ -740,6 +743,7 @@
      * @param   value   a floating-point number.
      * @return the bits that represent the floating-point number.
      */
+    @HotSpotIntrinsicCandidate
     public static int floatToIntBits(float value) {
         if (!isNaN(value)) {
             return floatToRawIntBits(value);
@@ -782,6 +786,7 @@
      * @return the bits that represent the floating-point number.
      * @since 1.3
      */
+    @HotSpotIntrinsicCandidate
     public static native int floatToRawIntBits(float value);
 
     /**
@@ -843,6 +848,7 @@
      * @return  the {@code float} floating-point value with the same bit
      *          pattern.
      */
+    @HotSpotIntrinsicCandidate
     public static native float intBitsToFloat(int bits);
 
     /**
--- a/jdk/src/java.base/share/classes/java/lang/Integer.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Integer.java	Thu Jul 09 22:46:18 2015 -0700
@@ -27,6 +27,7 @@
 
 import java.lang.annotation.Native;
 import java.util.Objects;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * The {@code Integer} class wraps a value of the primitive type
@@ -395,6 +396,7 @@
      * @param   i   an integer to be converted.
      * @return  a string representation of the argument in base&nbsp;10.
      */
+    @HotSpotIntrinsicCandidate
     public static String toString(int i) {
         if (i == Integer.MIN_VALUE)
             return "-2147483648";
@@ -972,6 +974,7 @@
      * @return an {@code Integer} instance representing {@code i}.
      * @since  1.5
      */
+    @HotSpotIntrinsicCandidate
     public static Integer valueOf(int i) {
         if (i >= IntegerCache.low && i <= IntegerCache.high)
             return IntegerCache.cache[i + (-IntegerCache.low)];
@@ -1035,6 +1038,7 @@
      * Returns the value of this {@code Integer} as an
      * {@code int}.
      */
+    @HotSpotIntrinsicCandidate
     public int intValue() {
         return value;
     }
@@ -1538,6 +1542,7 @@
      *     is equal to zero.
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static int numberOfLeadingZeros(int i) {
         // HD, Figure 5-6
         if (i == 0)
@@ -1565,6 +1570,7 @@
      *     to zero.
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static int numberOfTrailingZeros(int i) {
         // HD, Figure 5-14
         int y;
@@ -1587,6 +1593,7 @@
      *     representation of the specified {@code int} value.
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static int bitCount(int i) {
         // HD, Figure 5-2
         i = i - ((i >>> 1) & 0x55555555);
@@ -1688,6 +1695,7 @@
      *     {@code int} value.
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static int reverseBytes(int i) {
         return ((i >>> 24)           ) |
                ((i >>   8) &   0xFF00) |
--- a/jdk/src/java.base/share/classes/java/lang/Long.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Long.java	Thu Jul 09 22:46:18 2015 -0700
@@ -28,6 +28,7 @@
 import java.lang.annotation.Native;
 import java.math.*;
 import java.util.Objects;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 
 /**
@@ -1074,6 +1075,7 @@
      * @return a {@code Long} instance representing {@code l}.
      * @since  1.5
      */
+    @HotSpotIntrinsicCandidate
     public static Long valueOf(long l) {
         final int offset = 128;
         if (l >= -128 && l <= 127) { // will cache
@@ -1238,6 +1240,7 @@
      * Returns the value of this {@code Long} as a
      * {@code long} value.
      */
+    @HotSpotIntrinsicCandidate
     public long longValue() {
         return value;
     }
@@ -1655,6 +1658,7 @@
      *     is equal to zero.
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static int numberOfLeadingZeros(long i) {
         // HD, Figure 5-6
          if (i == 0)
@@ -1684,6 +1688,7 @@
      *     to zero.
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static int numberOfTrailingZeros(long i) {
         // HD, Figure 5-14
         int x, y;
@@ -1707,6 +1712,7 @@
      *     representation of the specified {@code long} value.
      * @since 1.5
      */
+     @HotSpotIntrinsicCandidate
      public static int bitCount(long i) {
         // HD, Figure 5-2
         i = i - ((i >>> 1) & 0x5555555555555555L);
@@ -1810,6 +1816,7 @@
      *     {@code long} value.
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static long reverseBytes(long i) {
         i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;
         return (i << 48) | ((i & 0xffff0000L) << 16) |
--- a/jdk/src/java.base/share/classes/java/lang/Math.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Math.java	Thu Jul 09 22:46:18 2015 -0700
@@ -24,10 +24,11 @@
  */
 
 package java.lang;
+
 import java.util.Random;
-
 import sun.misc.FloatConsts;
 import sun.misc.DoubleConsts;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * The class {@code Math} contains methods for performing basic
@@ -147,6 +148,7 @@
      * @param   a   an angle, in radians.
      * @return  the sine of the argument.
      */
+    @HotSpotIntrinsicCandidate
     public static double sin(double a) {
         return StrictMath.sin(a); // default impl. delegates to StrictMath
     }
@@ -162,6 +164,7 @@
      * @param   a   an angle, in radians.
      * @return  the cosine of the argument.
      */
+    @HotSpotIntrinsicCandidate
     public static double cos(double a) {
         return StrictMath.cos(a); // default impl. delegates to StrictMath
     }
@@ -179,6 +182,7 @@
      * @param   a   an angle, in radians.
      * @return  the tangent of the argument.
      */
+    @HotSpotIntrinsicCandidate
     public static double tan(double a) {
         return StrictMath.tan(a); // default impl. delegates to StrictMath
     }
@@ -280,6 +284,7 @@
      * @return  the value <i>e</i><sup>{@code a}</sup>,
      *          where <i>e</i> is the base of the natural logarithms.
      */
+    @HotSpotIntrinsicCandidate
     public static double exp(double a) {
         return StrictMath.exp(a); // default impl. delegates to StrictMath
     }
@@ -301,6 +306,7 @@
      * @return  the value ln&nbsp;{@code a}, the natural logarithm of
      *          {@code a}.
      */
+    @HotSpotIntrinsicCandidate
     public static double log(double a) {
         return StrictMath.log(a); // default impl. delegates to StrictMath
     }
@@ -326,6 +332,7 @@
      * @return  the base 10 logarithm of  {@code a}.
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static double log10(double a) {
         return StrictMath.log10(a); // default impl. delegates to StrictMath
     }
@@ -347,6 +354,7 @@
      * @return  the positive square root of {@code a}.
      *          If the argument is NaN or less than zero, the result is NaN.
      */
+    @HotSpotIntrinsicCandidate
     public static double sqrt(double a) {
         return StrictMath.sqrt(a); // default impl. delegates to StrictMath
                                    // Note that hardware sqrt instructions
@@ -525,6 +533,7 @@
      *          in polar coordinates that corresponds to the point
      *          (<i>x</i>,&nbsp;<i>y</i>) in Cartesian coordinates.
      */
+    @HotSpotIntrinsicCandidate
     public static double atan2(double y, double x) {
         return StrictMath.atan2(y, x); // default impl. delegates to StrictMath
     }
@@ -652,6 +661,7 @@
      * @param   b   the exponent.
      * @return  the value {@code a}<sup>{@code b}</sup>.
      */
+    @HotSpotIntrinsicCandidate
     public static double pow(double a, double b) {
         return StrictMath.pow(a, b); // default impl. delegates to StrictMath
     }
@@ -806,6 +816,7 @@
      * @throws ArithmeticException if the result overflows an int
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static int addExact(int x, int y) {
         int r = x + y;
         // HD 2-12 Overflow iff both arguments have the opposite sign of the result
@@ -825,6 +836,7 @@
      * @throws ArithmeticException if the result overflows a long
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static long addExact(long x, long y) {
         long r = x + y;
         // HD 2-12 Overflow iff both arguments have the opposite sign of the result
@@ -844,6 +856,7 @@
      * @throws ArithmeticException if the result overflows an int
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static int subtractExact(int x, int y) {
         int r = x - y;
         // HD 2-12 Overflow iff the arguments have different signs and
@@ -864,6 +877,7 @@
      * @throws ArithmeticException if the result overflows a long
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static long subtractExact(long x, long y) {
         long r = x - y;
         // HD 2-12 Overflow iff the arguments have different signs and
@@ -884,6 +898,7 @@
      * @throws ArithmeticException if the result overflows an int
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static int multiplyExact(int x, int y) {
         long r = (long)x * (long)y;
         if ((int)r != r) {
@@ -902,6 +917,7 @@
      * @throws ArithmeticException if the result overflows a long
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static long multiplyExact(long x, long y) {
         long r = x * y;
         long ax = Math.abs(x);
@@ -927,6 +943,7 @@
      * @throws ArithmeticException if the result overflows an int
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static int incrementExact(int a) {
         if (a == Integer.MAX_VALUE) {
             throw new ArithmeticException("integer overflow");
@@ -944,6 +961,7 @@
      * @throws ArithmeticException if the result overflows a long
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static long incrementExact(long a) {
         if (a == Long.MAX_VALUE) {
             throw new ArithmeticException("long overflow");
@@ -961,6 +979,7 @@
      * @throws ArithmeticException if the result overflows an int
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static int decrementExact(int a) {
         if (a == Integer.MIN_VALUE) {
             throw new ArithmeticException("integer overflow");
@@ -978,6 +997,7 @@
      * @throws ArithmeticException if the result overflows a long
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static long decrementExact(long a) {
         if (a == Long.MIN_VALUE) {
             throw new ArithmeticException("long overflow");
@@ -995,6 +1015,7 @@
      * @throws ArithmeticException if the result overflows an int
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static int negateExact(int a) {
         if (a == Integer.MIN_VALUE) {
             throw new ArithmeticException("integer overflow");
@@ -1012,6 +1033,7 @@
      * @throws ArithmeticException if the result overflows a long
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public static long negateExact(long a) {
         if (a == Long.MIN_VALUE) {
             throw new ArithmeticException("long overflow");
@@ -1256,6 +1278,7 @@
      * @param   a   the argument whose absolute value is to be determined
      * @return  the absolute value of the argument.
      */
+    @HotSpotIntrinsicCandidate
     public static double abs(double a) {
         return (a <= 0.0D) ? 0.0D - a : a;
     }
@@ -1270,6 +1293,7 @@
      * @param   b   another argument.
      * @return  the larger of {@code a} and {@code b}.
      */
+    @HotSpotIntrinsicCandidate
     public static int max(int a, int b) {
         return (a >= b) ? a : b;
     }
@@ -1354,6 +1378,7 @@
      * @param   b   another argument.
      * @return  the smaller of {@code a} and {@code b}.
      */
+    @HotSpotIntrinsicCandidate
     public static int min(int a, int b) {
         return (a <= b) ? a : b;
     }
--- a/jdk/src/java.base/share/classes/java/lang/Object.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Object.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,6 +25,8 @@
 
 package java.lang;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
+
 /**
  * Class {@code Object} is the root of the class hierarchy.
  * Every class has {@code Object} as a superclass. All objects,
@@ -44,6 +46,7 @@
     /**
      * Constructs a new object.
      */
+    @HotSpotIntrinsicCandidate
     public Object() {}
 
     /**
@@ -65,6 +68,7 @@
      *         class of this object.
      * @jls 15.8.2 Class Literals
      */
+    @HotSpotIntrinsicCandidate
     public final native Class<?> getClass();
 
     /**
@@ -101,6 +105,7 @@
      * @see     java.lang.Object#equals(java.lang.Object)
      * @see     java.lang.System#identityHashCode
      */
+    @HotSpotIntrinsicCandidate
     public native int hashCode();
 
     /**
@@ -213,6 +218,7 @@
      *               be cloned.
      * @see java.lang.Cloneable
      */
+    @HotSpotIntrinsicCandidate
     protected native Object clone() throws CloneNotSupportedException;
 
     /**
--- a/jdk/src/java.base/share/classes/java/lang/Short.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Short.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,6 +25,8 @@
 
 package java.lang;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
+
 /**
  * The {@code Short} class wraps a value of primitive type {@code
  * short} in an object.  An object of type {@code Short} contains a
@@ -227,6 +229,7 @@
      * @return a {@code Short} instance representing {@code s}.
      * @since  1.5
      */
+    @HotSpotIntrinsicCandidate
     public static Short valueOf(short s) {
         final int offset = 128;
         int sAsInt = s;
@@ -334,6 +337,7 @@
      * Returns the value of this {@code Short} as a
      * {@code short}.
      */
+    @HotSpotIntrinsicCandidate
     public short shortValue() {
         return value;
     }
@@ -487,6 +491,7 @@
      *     the bytes in the specified {@code short} value.
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static short reverseBytes(short i) {
         return (short) (((i & 0xFF00) >> 8) | (i << 8));
     }
--- a/jdk/src/java.base/share/classes/java/lang/StrictMath.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/StrictMath.java	Thu Jul 09 22:46:18 2015 -0700
@@ -24,8 +24,10 @@
  */
 
 package java.lang;
+
 import java.util.Random;
 import sun.misc.DoubleConsts;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * The class {@code StrictMath} contains methods for performing basic
@@ -243,7 +245,6 @@
      */
     public static native double log(double a);
 
-
     /**
      * Returns the base 10 logarithm of a {@code double} value.
      * Special cases:
@@ -280,6 +281,7 @@
      * @param   a   a value.
      * @return  the positive square root of {@code a}.
      */
+    @HotSpotIntrinsicCandidate
     public static native double sqrt(double a);
 
     /**
@@ -521,7 +523,6 @@
      */
     public static native double atan2(double y, double x);
 
-
     /**
      * Returns the value of the first argument raised to the power of the
      * second argument. Special cases:
@@ -1009,6 +1010,7 @@
      * @param   b   another argument.
      * @return  the larger of {@code a} and {@code b}.
      */
+    @HotSpotIntrinsicCandidate
     public static int max(int a, int b) {
         return Math.max(a, b);
     }
@@ -1073,6 +1075,7 @@
      * @param   b   another argument.
      * @return  the smaller of {@code a} and {@code b}.
      */
+    @HotSpotIntrinsicCandidate
     public static int min(int a, int b) {
         return Math.min(a, b);
     }
--- a/jdk/src/java.base/share/classes/java/lang/String.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/String.java	Thu Jul 09 22:46:18 2015 -0700
@@ -42,6 +42,7 @@
 import java.util.regex.PatternSyntaxException;
 import java.util.stream.IntStream;
 import java.util.stream.StreamSupport;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * The {@code String} class represents character strings. All
@@ -152,6 +153,7 @@
      * @param  original
      *         A {@code String}
      */
+    @HotSpotIntrinsicCandidate
     public String(String original) {
         this.value = original.value;
         this.hash = original.hash;
@@ -978,6 +980,7 @@
      * @see  #compareTo(String)
      * @see  #equalsIgnoreCase(String)
      */
+    @HotSpotIntrinsicCandidate
     public boolean equals(Object anObject) {
         if (this == anObject) {
             return true;
@@ -1154,6 +1157,7 @@
      *          value greater than {@code 0} if this string is
      *          lexicographically greater than the string argument.
      */
+    @HotSpotIntrinsicCandidate
     public int compareTo(String anotherString) {
         char[] v1 = value;
         char[] v2 = anotherString.value;
@@ -1696,6 +1700,7 @@
      * @return  the index of the first occurrence of the specified substring,
      *          or {@code -1} if there is no such occurrence.
      */
+    @HotSpotIntrinsicCandidate
     public int indexOf(String str) {
         return indexOf(str, 0);
     }
--- a/jdk/src/java.base/share/classes/java/lang/StringBuffer.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/StringBuffer.java	Thu Jul 09 22:46:18 2015 -0700
@@ -26,6 +26,7 @@
 package java.lang;
 
 import java.util.Arrays;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * A thread-safe, mutable sequence of characters.
@@ -112,6 +113,7 @@
      * Constructs a string buffer with no characters in it and an
      * initial capacity of 16 characters.
      */
+    @HotSpotIntrinsicCandidate
     public StringBuffer() {
         super(16);
     }
@@ -124,6 +126,7 @@
      * @exception  NegativeArraySizeException  if the {@code capacity}
      *               argument is less than {@code 0}.
      */
+    @HotSpotIntrinsicCandidate
     public StringBuffer(int capacity) {
         super(capacity);
     }
@@ -135,6 +138,7 @@
      *
      * @param   str   the initial contents of the buffer.
      */
+    @HotSpotIntrinsicCandidate
     public StringBuffer(String str) {
         super(str.length() + 16);
         append(str);
@@ -271,6 +275,7 @@
     }
 
     @Override
+    @HotSpotIntrinsicCandidate
     public synchronized StringBuffer append(String str) {
         toStringCache = null;
         super.append(str);
@@ -382,6 +387,7 @@
     }
 
     @Override
+    @HotSpotIntrinsicCandidate
     public synchronized StringBuffer append(char c) {
         toStringCache = null;
         super.append(c);
@@ -389,6 +395,7 @@
     }
 
     @Override
+    @HotSpotIntrinsicCandidate
     public synchronized StringBuffer append(int i) {
         toStringCache = null;
         super.append(i);
@@ -670,6 +677,7 @@
     }
 
     @Override
+    @HotSpotIntrinsicCandidate
     public synchronized String toString() {
         if (toStringCache == null) {
             toStringCache = Arrays.copyOfRange(value, 0, count);
--- a/jdk/src/java.base/share/classes/java/lang/StringBuilder.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/StringBuilder.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,6 +25,7 @@
 
 package java.lang;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * A mutable sequence of characters.  This class provides an API compatible
@@ -85,6 +86,7 @@
      * Constructs a string builder with no characters in it and an
      * initial capacity of 16 characters.
      */
+    @HotSpotIntrinsicCandidate
     public StringBuilder() {
         super(16);
     }
@@ -97,6 +99,7 @@
      * @throws     NegativeArraySizeException  if the {@code capacity}
      *               argument is less than {@code 0}.
      */
+    @HotSpotIntrinsicCandidate
     public StringBuilder(int capacity) {
         super(capacity);
     }
@@ -108,6 +111,7 @@
      *
      * @param   str   the initial contents of the buffer.
      */
+    @HotSpotIntrinsicCandidate
     public StringBuilder(String str) {
         super(str.length() + 16);
         append(str);
@@ -132,6 +136,7 @@
     }
 
     @Override
+    @HotSpotIntrinsicCandidate
     public StringBuilder append(String str) {
         super.append(str);
         return this;
@@ -198,12 +203,14 @@
     }
 
     @Override
+    @HotSpotIntrinsicCandidate
     public StringBuilder append(char c) {
         super.append(c);
         return this;
     }
 
     @Override
+    @HotSpotIntrinsicCandidate
     public StringBuilder append(int i) {
         super.append(i);
         return this;
@@ -402,6 +409,7 @@
     }
 
     @Override
+    @HotSpotIntrinsicCandidate
     public String toString() {
         // Create a copy, don't share the array
         return new String(value, 0, count);
--- a/jdk/src/java.base/share/classes/java/lang/System.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/System.java	Thu Jul 09 22:46:18 2015 -0700
@@ -42,6 +42,7 @@
 import sun.reflect.Reflection;
 import sun.security.util.SecurityConstants;
 import sun.reflect.annotation.AnnotationType;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * The <code>System</code> class contains several useful class fields
@@ -349,6 +350,7 @@
      *          the current time and midnight, January 1, 1970 UTC.
      * @see     java.util.Date
      */
+    @HotSpotIntrinsicCandidate
     public static native long currentTimeMillis();
 
     /**
@@ -392,6 +394,7 @@
      *         high-resolution time source, in nanoseconds
      * @since 1.5
      */
+    @HotSpotIntrinsicCandidate
     public static native long nanoTime();
 
     /**
@@ -486,6 +489,7 @@
      * @exception  NullPointerException if either <code>src</code> or
      *               <code>dest</code> is <code>null</code>.
      */
+    @HotSpotIntrinsicCandidate
     public static native void arraycopy(Object src,  int  srcPos,
                                         Object dest, int destPos,
                                         int length);
@@ -501,6 +505,7 @@
      * @return  the hashCode
      * @since   1.1
      */
+    @HotSpotIntrinsicCandidate
     public static native int identityHashCode(Object x);
 
     /**
--- a/jdk/src/java.base/share/classes/java/lang/Thread.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/Thread.java	Thu Jul 09 22:46:18 2015 -0700
@@ -40,7 +40,7 @@
 import sun.reflect.CallerSensitive;
 import sun.reflect.Reflection;
 import sun.security.util.SecurityConstants;
-
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * A <i>thread</i> is a thread of execution in a program. The Java
@@ -261,6 +261,7 @@
      *
      * @return  the currently executing thread.
      */
+    @HotSpotIntrinsicCandidate
     public static native Thread currentThread();
 
     /**
@@ -966,6 +967,7 @@
      * is reset or not based on the value of ClearInterrupted that is
      * passed.
      */
+    @HotSpotIntrinsicCandidate
     private native boolean isInterrupted(boolean ClearInterrupted);
 
     /**
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Thu Jul 09 22:46:18 2015 -0700
@@ -27,6 +27,7 @@
 
 
 import java.util.*;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 import static java.lang.invoke.MethodHandleStatics.*;
 
@@ -476,6 +477,7 @@
      * @throws WrongMethodTypeException if the target's type is not identical with the caller's symbolic type descriptor
      * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
      */
+    @HotSpotIntrinsicCandidate
     public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
 
     /**
@@ -513,6 +515,7 @@
      * @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails
      * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
      */
+    @HotSpotIntrinsicCandidate
     public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
 
     /**
@@ -532,6 +535,7 @@
      * @param args the signature-polymorphic parameter list, statically represented using varargs
      * @return the signature-polymorphic result, statically represented using {@code Object}
      */
+    @HotSpotIntrinsicCandidate
     /*non-public*/ final native @PolymorphicSignature Object invokeBasic(Object... args) throws Throwable;
 
     /**
@@ -541,6 +545,7 @@
      * @param args the signature-polymorphic parameter list, statically represented using varargs
      * @return the signature-polymorphic result, statically represented using {@code Object}
      */
+    @HotSpotIntrinsicCandidate
     /*non-public*/ static native @PolymorphicSignature Object linkToVirtual(Object... args) throws Throwable;
 
     /**
@@ -550,6 +555,7 @@
      * @param args the signature-polymorphic parameter list, statically represented using varargs
      * @return the signature-polymorphic result, statically represented using {@code Object}
      */
+    @HotSpotIntrinsicCandidate
     /*non-public*/ static native @PolymorphicSignature Object linkToStatic(Object... args) throws Throwable;
 
     /**
@@ -559,6 +565,7 @@
      * @param args the signature-polymorphic parameter list, statically represented using varargs
      * @return the signature-polymorphic result, statically represented using {@code Object}
      */
+    @HotSpotIntrinsicCandidate
     /*non-public*/ static native @PolymorphicSignature Object linkToSpecial(Object... args) throws Throwable;
 
     /**
@@ -568,6 +575,7 @@
      * @param args the signature-polymorphic parameter list, statically represented using varargs
      * @return the signature-polymorphic result, statically represented using {@code Object}
      */
+    @HotSpotIntrinsicCandidate
     /*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args) throws Throwable;
 
     /**
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Jul 09 22:46:18 2015 -0700
@@ -36,6 +36,7 @@
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyType;
 import sun.invoke.util.Wrapper;
+import jdk.internal.HotSpotIntrinsicCandidate;
 import sun.reflect.CallerSensitive;
 import sun.reflect.Reflection;
 import static java.lang.invoke.LambdaForm.*;
@@ -709,6 +710,7 @@
 
     // Intrinsified by C2. Counters are used during parsing to calculate branch frequencies.
     @LambdaForm.Hidden
+    @jdk.internal.HotSpotIntrinsicCandidate
     static
     boolean profileBoolean(boolean result, int[] counters) {
         // Profile is int[2] where [0] and [1] correspond to false and true occurrences respectively.
@@ -724,6 +726,7 @@
 
     // Intrinsified by C2. Returns true if obj is a compile-time constant.
     @LambdaForm.Hidden
+    @jdk.internal.HotSpotIntrinsicCandidate
     static
     boolean isCompileConstant(Object obj) {
         return false;
--- a/jdk/src/java.base/share/classes/java/lang/ref/Reference.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/ref/Reference.java	Thu Jul 09 22:46:18 2015 -0700
@@ -29,6 +29,7 @@
 import sun.misc.JavaLangRefAccess;
 import sun.misc.ManagedLocalsThread;
 import sun.misc.SharedSecrets;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * Abstract base class for reference objects.  This class defines the
@@ -251,6 +252,7 @@
      * @return   The object to which this reference refers, or
      *           <code>null</code> if this reference object has been cleared
      */
+    @HotSpotIntrinsicCandidate
     public T get() {
         return this.referent;
     }
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Array.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/Array.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,6 +25,8 @@
 
 package java.lang.reflect;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
+
 /**
  * The {@code Array} class provides static methods to dynamically create and
  * access Java arrays.
@@ -119,6 +121,7 @@
      * @exception IllegalArgumentException if the object argument is not
      * an array
      */
+    @HotSpotIntrinsicCandidate
     public static native int getLength(Object array)
         throws IllegalArgumentException;
 
@@ -477,6 +480,7 @@
      * Private
      */
 
+    @HotSpotIntrinsicCandidate
     private static native Object newArray(Class<?> componentType, int length)
         throws NegativeArraySizeException;
 
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Method.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/Method.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,6 +25,7 @@
 
 package java.lang.reflect;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
 import sun.reflect.CallerSensitive;
 import sun.reflect.MethodAccessor;
 import sun.reflect.Reflection;
@@ -485,6 +486,7 @@
      * provoked by this method fails.
      */
     @CallerSensitive
+    @HotSpotIntrinsicCandidate
     public Object invoke(Object obj, Object... args)
         throws IllegalAccessException, IllegalArgumentException,
            InvocationTargetException
--- a/jdk/src/java.base/share/classes/java/math/BigInteger.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/math/BigInteger.java	Thu Jul 09 22:46:18 2015 -0700
@@ -34,10 +34,13 @@
 import java.io.ObjectOutputStream;
 import java.io.ObjectStreamField;
 import java.util.Arrays;
+import java.util.Objects;
 import java.util.Random;
 import java.util.concurrent.ThreadLocalRandom;
+
 import sun.misc.DoubleConsts;
 import sun.misc.FloatConsts;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * Immutable arbitrary-precision integers.  All operations behave as if
@@ -262,6 +265,15 @@
      */
     private static final int MULTIPLY_SQUARE_THRESHOLD = 20;
 
+    /**
+     * The threshold for using an intrinsic version of
+     * implMontgomeryXXX to perform Montgomery multiplication.  If the
+     * number of ints in the number is more than this value we do not
+     * use the intrinsic.
+     */
+    private static final int MONTGOMERY_INTRINSIC_THRESHOLD = 512;
+
+
     // Constructors
 
     /**
@@ -1639,7 +1651,14 @@
      * Multiplies int arrays x and y to the specified lengths and places
      * the result into z. There will be no leading zeros in the resultant array.
      */
-    private int[] multiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z) {
+    private static int[] multiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z) {
+        multiplyToLenCheck(x, xlen);
+        multiplyToLenCheck(y, ylen);
+        return implMultiplyToLen(x, xlen, y, ylen, z);
+    }
+
+    @HotSpotIntrinsicCandidate
+    private static int[] implMultiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z) {
         int xstart = xlen - 1;
         int ystart = ylen - 1;
 
@@ -1669,6 +1688,18 @@
         return z;
     }
 
+    private static void multiplyToLenCheck(int[] array, int length) {
+        if (length <= 0) {
+            return;  // not an error because multiplyToLen won't execute if len <= 0
+        }
+
+        Objects.requireNonNull(array);
+
+        if (length > array.length) {
+            throw new ArrayIndexOutOfBoundsException(length - 1);
+        }
+    }
+
     /**
      * Multiplies two BigIntegers using the Karatsuba multiplication
      * algorithm.  This is a recursive divide-and-conquer algorithm which is
@@ -1999,6 +2030,7 @@
      /**
       * Java Runtime may use intrinsic for this method.
       */
+     @HotSpotIntrinsicCandidate
      private static final int[] implSquareToLen(int[] x, int len, int[] z, int zlen) {
         /*
          * The algorithm used here is adapted from Colin Plumb's C library.
@@ -2601,6 +2633,77 @@
         return (invertResult ? result.modInverse(m) : result);
     }
 
+    // Montgomery multiplication.  These are wrappers for
+    // implMontgomeryXX routines which are expected to be replaced by
+    // virtual machine intrinsics.  We don't use the intrinsics for
+    // very large operands: MONTGOMERY_INTRINSIC_THRESHOLD should be
+    // larger than any reasonable crypto key.
+    private static int[] montgomeryMultiply(int[] a, int[] b, int[] n, int len, long inv,
+                                            int[] product) {
+        implMontgomeryMultiplyChecks(a, b, n, len, product);
+        if (len > MONTGOMERY_INTRINSIC_THRESHOLD) {
+            // Very long argument: do not use an intrinsic
+            product = multiplyToLen(a, len, b, len, product);
+            return montReduce(product, n, len, (int)inv);
+        } else {
+            return implMontgomeryMultiply(a, b, n, len, inv, materialize(product, len));
+        }
+    }
+    private static int[] montgomerySquare(int[] a, int[] n, int len, long inv,
+                                          int[] product) {
+        implMontgomeryMultiplyChecks(a, a, n, len, product);
+        if (len > MONTGOMERY_INTRINSIC_THRESHOLD) {
+            // Very long argument: do not use an intrinsic
+            product = squareToLen(a, len, product);
+            return montReduce(product, n, len, (int)inv);
+        } else {
+            return implMontgomerySquare(a, n, len, inv, materialize(product, len));
+        }
+    }
+
+    // Range-check everything.
+    private static void implMontgomeryMultiplyChecks
+        (int[] a, int[] b, int[] n, int len, int[] product) throws RuntimeException {
+        if (len % 2 != 0) {
+            throw new IllegalArgumentException("input array length must be even: " + len);
+        }
+
+        if (len < 1) {
+            throw new IllegalArgumentException("invalid input length: " + len);
+        }
+
+        if (len > a.length ||
+            len > b.length ||
+            len > n.length ||
+            (product != null && len > product.length)) {
+            throw new IllegalArgumentException("input array length out of bound: " + len);
+        }
+    }
+
+    // Make sure that the int array z (which is expected to contain
+    // the result of a Montgomery multiplication) is present and
+    // sufficiently large.
+    private static int[] materialize(int[] z, int len) {
+         if (z == null || z.length < len)
+             z = new int[len];
+         return z;
+    }
+
+    // These methods are intended to be be replaced by virtual machine
+    // intrinsics.
+    @HotSpotIntrinsicCandidate
+    private static int[] implMontgomeryMultiply(int[] a, int[] b, int[] n, int len,
+                                         long inv, int[] product) {
+        product = multiplyToLen(a, len, b, len, product);
+        return montReduce(product, n, len, (int)inv);
+    }
+    @HotSpotIntrinsicCandidate
+    private static int[] implMontgomerySquare(int[] a, int[] n, int len,
+                                       long inv, int[] product) {
+        product = squareToLen(a, len, product);
+        return montReduce(product, n, len, (int)inv);
+    }
+
     static int[] bnExpModThreshTable = {7, 25, 81, 241, 673, 1793,
                                                 Integer.MAX_VALUE}; // Sentinel
 
@@ -2679,6 +2782,17 @@
         int[] mod = z.mag;
         int modLen = mod.length;
 
+        // Make modLen even. It is conventional to use a cryptographic
+        // modulus that is 512, 768, 1024, or 2048 bits, so this code
+        // will not normally be executed. However, it is necessary for
+        // the correct functioning of the HotSpot intrinsics.
+        if ((modLen & 1) != 0) {
+            int[] x = new int[modLen + 1];
+            System.arraycopy(mod, 0, x, 1, modLen);
+            mod = x;
+            modLen++;
+        }
+
         // Select an appropriate window size
         int wbits = 0;
         int ebits = bitLength(exp, exp.length);
@@ -2697,8 +2811,10 @@
         for (int i=0; i < tblmask; i++)
             table[i] = new int[modLen];
 
-        // Compute the modular inverse
-        int inv = -MutableBigInteger.inverseMod32(mod[modLen-1]);
+        // Compute the modular inverse of the least significant 64-bit
+        // digit of the modulus
+        long n0 = (mod[modLen-1] & LONG_MASK) + ((mod[modLen-2] & LONG_MASK) << 32);
+        long inv = -MutableBigInteger.inverseMod64(n0);
 
         // Convert base to Montgomery form
         int[] a = leftShift(base, base.length, modLen << 5);
@@ -2706,6 +2822,8 @@
         MutableBigInteger q = new MutableBigInteger(),
                           a2 = new MutableBigInteger(a),
                           b2 = new MutableBigInteger(mod);
+        b2.normalize(); // MutableBigInteger.divide() assumes that its
+                        // divisor is in normal form.
 
         MutableBigInteger r= a2.divide(b2, q);
         table[0] = r.toIntArray();
@@ -2714,22 +2832,19 @@
         if (table[0].length < modLen) {
            int offset = modLen - table[0].length;
            int[] t2 = new int[modLen];
-           for (int i=0; i < table[0].length; i++)
-               t2[i+offset] = table[0][i];
+           System.arraycopy(table[0], 0, t2, offset, table[0].length);
            table[0] = t2;
         }
 
         // Set b to the square of the base
-        int[] b = squareToLen(table[0], modLen, null);
-        b = montReduce(b, mod, modLen, inv);
+        int[] b = montgomerySquare(table[0], mod, modLen, inv, null);
 
         // Set t to high half of b
         int[] t = Arrays.copyOf(b, modLen);
 
         // Fill in the table with odd powers of the base
         for (int i=1; i < tblmask; i++) {
-            int[] prod = multiplyToLen(t, modLen, table[i-1], modLen, null);
-            table[i] = montReduce(prod, mod, modLen, inv);
+            table[i] = montgomeryMultiply(t, table[i-1], mod, modLen, inv, null);
         }
 
         // Pre load the window that slides over the exponent
@@ -2800,8 +2915,7 @@
                     isone = false;
                 } else {
                     t = b;
-                    a = multiplyToLen(t, modLen, mult, modLen, a);
-                    a = montReduce(a, mod, modLen, inv);
+                    a = montgomeryMultiply(t, mult, mod, modLen, inv, a);
                     t = a; a = b; b = t;
                 }
             }
@@ -2813,8 +2927,7 @@
             // Square the input
             if (!isone) {
                 t = b;
-                a = squareToLen(t, modLen, a);
-                a = montReduce(a, mod, modLen, inv);
+                a = montgomerySquare(t, mod, modLen, inv, a);
                 t = a; a = b; b = t;
             }
         }
@@ -2823,7 +2936,7 @@
         int[] t2 = new int[2*modLen];
         System.arraycopy(b, 0, t2, modLen, modLen);
 
-        b = montReduce(t2, mod, modLen, inv);
+        b = montReduce(t2, mod, modLen, (int)inv);
 
         t2 = Arrays.copyOf(b, modLen);
 
@@ -2916,6 +3029,7 @@
     /**
      * Java Runtime may use intrinsic for this method.
      */
+    @HotSpotIntrinsicCandidate
     private static int implMulAdd(int[] out, int[] in, int offset, int len, int k) {
         long kLong = k & LONG_MASK;
         long carry = 0;
--- a/jdk/src/java.base/share/classes/java/math/MutableBigInteger.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/math/MutableBigInteger.java	Thu Jul 09 22:46:18 2015 -0700
@@ -2065,6 +2065,21 @@
     }
 
     /**
+     * Returns the multiplicative inverse of val mod 2^64.  Assumes val is odd.
+     */
+    static long inverseMod64(long val) {
+        // Newton's iteration!
+        long t = val;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        assert(t * val == 1);
+        return t;
+    }
+
+    /**
      * Calculate the multiplicative inverse of 2^k mod mod, where mod is odd.
      */
     static MutableBigInteger modInverseBP2(MutableBigInteger mod, int k) {
--- a/jdk/src/java.base/share/classes/java/nio/Buffer.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/nio/Buffer.java	Thu Jul 09 22:46:18 2015 -0700
@@ -26,6 +26,7 @@
 package java.nio;
 
 import java.util.Spliterator;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * A container for data of a specific primitive type.
@@ -535,6 +536,7 @@
      * IndexOutOfBoundsException} if it is not smaller than the limit
      * or is smaller than zero.
      */
+    @HotSpotIntrinsicCandidate
     final int checkIndex(int i) {                       // package-private
         if ((i < 0) || (i >= limit))
             throw new IndexOutOfBoundsException();
--- a/jdk/src/java.base/share/classes/java/util/Arrays.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/util/Arrays.java	Thu Jul 09 22:46:18 2015 -0700
@@ -42,6 +42,7 @@
 import java.util.stream.LongStream;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * This class contains various methods for manipulating arrays (such as
@@ -2654,6 +2655,7 @@
      * @param a2 the other array to be tested for equality
      * @return <tt>true</tt> if the two arrays are equal
      */
+    @HotSpotIntrinsicCandidate
     public static boolean equals(char[] a, char[] a2) {
         if (a==a2)
             return true;
@@ -3205,6 +3207,7 @@
      *     an array of class <tt>newType</tt>
      * @since 1.6
      */
+    @HotSpotIntrinsicCandidate
     public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
         @SuppressWarnings("unchecked")
         T[] copy = ((Object)newType == (Object)Object[].class)
@@ -3474,6 +3477,7 @@
      *     an array of class <tt>newType</tt>.
      * @since 1.6
      */
+    @HotSpotIntrinsicCandidate
     public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
         int newLength = to - from;
         if (newLength < 0)
--- a/jdk/src/java.base/share/classes/java/util/stream/Streams.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/util/stream/Streams.java	Thu Jul 09 22:46:18 2015 -0700
@@ -31,6 +31,7 @@
 import java.util.function.DoubleConsumer;
 import java.util.function.IntConsumer;
 import java.util.function.LongConsumer;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * Utility methods for operating on and creating streams.
@@ -98,6 +99,7 @@
         }
 
         @Override
+        @HotSpotIntrinsicCandidate
         public void forEachRemaining(IntConsumer consumer) {
             Objects.requireNonNull(consumer);
 
--- a/jdk/src/java.base/share/classes/java/util/zip/CRC32.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/util/zip/CRC32.java	Thu Jul 09 22:46:18 2015 -0700
@@ -26,7 +26,10 @@
 package java.util.zip;
 
 import java.nio.ByteBuffer;
+import java.util.Objects;
+
 import sun.nio.ch.DirectBuffer;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * A class that can be used to compute the CRC-32 of a data stream.
@@ -123,9 +126,49 @@
         return (long)crc & 0xffffffffL;
     }
 
+    @HotSpotIntrinsicCandidate
     private native static int update(int crc, int b);
-    private native static int updateBytes(int crc, byte[] b, int off, int len);
+
+    private static int updateBytes(int crc, byte[] b, int off, int len) {
+        updateBytesCheck(b, off, len);
+        return updateBytes0(crc, b, off, len);
+    }
+
+    @HotSpotIntrinsicCandidate
+    private native static int updateBytes0(int crc, byte[] b, int off, int len);
+
+    private static void updateBytesCheck(byte[] b, int off, int len) {
+        if (len <= 0) {
+            return;  // not an error because updateBytesImpl won't execute if len <= 0
+        }
+
+        Objects.requireNonNull(b);
+
+        if (off < 0 || off >= b.length) {
+            throw new ArrayIndexOutOfBoundsException(off);
+        }
 
-    private native static int updateByteBuffer(int adler, long addr,
-                                               int off, int len);
+        int endIndex = off + len - 1;
+        if (endIndex < 0 || endIndex >= b.length) {
+            throw new ArrayIndexOutOfBoundsException(endIndex);
+        }
+    }
+
+    private static int updateByteBuffer(int alder, long addr,
+                                        int off, int len) {
+        updateByteBufferCheck(addr);
+        return updateByteBuffer0(alder, addr, off, len);
+    }
+
+    @HotSpotIntrinsicCandidate
+    private native static int updateByteBuffer0(int alder, long addr,
+                                                int off, int len);
+
+    private static void updateByteBufferCheck(long addr) {
+        // Performs only a null check because bounds checks
+        // are not easy to do on raw addresses.
+        if (addr == 0L) {
+            throw new NullPointerException();
+        }
+    }
 }
--- a/jdk/src/java.base/share/classes/java/util/zip/CRC32C.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/util/zip/CRC32C.java	Thu Jul 09 22:46:18 2015 -0700
@@ -26,6 +26,8 @@
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+
+import jdk.internal.HotSpotIntrinsicCandidate;
 import sun.misc.Unsafe;
 import sun.nio.ch.DirectBuffer;
 
@@ -204,6 +206,7 @@
     /**
      * Updates the CRC-32C checksum with the specified array of bytes.
      */
+    @HotSpotIntrinsicCandidate
     private static int updateBytes(int crc, byte[] b, int off, int end) {
 
         // Do only byte reads for arrays so short they can't be aligned
@@ -278,6 +281,7 @@
     /**
      * Updates the CRC-32C checksum reading from the specified address.
      */
+    @HotSpotIntrinsicCandidate
     private static int updateDirectByteBuffer(int crc, long address,
                                               int off, int end) {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/HotSpotIntrinsicCandidate.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal;
+
+import java.lang.annotation.*;
+
+/**
+ * The {@code @HotSpotIntrinsicCandidate} annotation is specific to the Oracle Java
+ * HotSpot Virtual Machine implementation and indicates that an annotated method
+ * may be (but is not guaranteed to be) intrinsified by the HotSpot VM. A method
+ * is intrinsified if the HotSpot VM replaces the annotated method with hand-written
+ * assembly and/or hand-written compiler IR -- a compiler intrinsic -- to improve
+ * performance. The {@code @HotSpotIntrinsicCandidate} annotation is internal to the
+ * Java libraries and is therefore not supposed to have any relevance for application
+ * code.
+ *
+ * Maintainers of the Java libraries must consider the following when
+ * modifying methods annotated with {@code @HotSpotIntrinsicCandidate}.
+ *
+ * <ul>
+ * <li>When modifying a method annotated with {@code @HotSpotIntrinsicCandidate},
+ * the corresponding intrinsic code in the HotSpot VM implementation must be
+ * updated to match the semantics of the annotated method.</li>
+ * <li>For some annotated methods, the corresponding intrinsic may omit some low-level
+ * checks that would be performed as a matter of course if the intrinsic is implemented
+ * using Java bytecodes. This is because individual Java bytecodes implicitly check
+ * for exceptions like {@code NullPointerException} and {@code ArrayStoreException}.
+ * If such a method is replaced by an intrinsic coded in assembly language, any
+ * checks performed as a matter of normal bytecode operation must be performed
+ * before entry into the assembly code. These checks must be performed, as
+ * appropriate, on all arguments to the intrinsic, and on other values (if any) obtained
+ * by the intrinsic through those arguments. The checks may be deduced by inspecting
+ * the non-intrinsic Java code for the method, and determining exactly which exceptions
+ * may be thrown by the code, including undeclared implicit {@code RuntimeException}s.
+ * Therefore, depending on the data accesses performed by the intrinsic,
+ * the checks may include:
+ *
+ *  <ul>
+ *  <li>null checks on references</li>
+ *  <li>range checks on primitive values used as array indexes</li>
+ *  <li>other validity checks on primitive values (e.g., for divide-by-zero conditions)</li>
+ *  <li>store checks on reference values stored into arrays</li>
+ *  <li>array length checks on arrays indexed from within the intrinsic</li>
+ *  <li>reference casts (when formal parameters are {@code Object} or some other weak type)</li>
+ *  </ul>
+ *
+ * </li>
+ *
+ * <li>Note that the receiver value ({@code this}) is passed as a extra argument
+ * to all non-static methods. If a non-static method is an intrinsic, the receiver
+ * value does not need a null check, but (as stated above) any values loaded by the
+ * intrinsic from object fields must also be checked. As a matter of clarity, it is
+ * better to make intrinisics be static methods, to make the dependency on {@code this}
+ * clear. Also, it is better to explicitly load all required values from object
+ * fields before entering the intrinsic code, and pass those values as explicit arguments.
+ * First, this may be necessary for null checks (or other checks). Second, if the
+ * intrinsic reloads the values from fields and operates on those without checks,
+ * race conditions may be able to introduce unchecked invalid values into the intrinsic.
+ * If the intrinsic needs to store a value back to an object field, that value should be
+ * returned explicitly from the intrinsic; if there are multiple return values, coders
+ * should consider buffering them in an array. Removing field access from intrinsics
+ * not only clarifies the interface with between the JVM and JDK; it also helps decouple
+ * the HotSpot and JDK implementations, since if JDK code before and after the intrinsic
+ * manages all field accesses, then intrinsics can be coded to be agnostic of object
+ * layouts.</li>
+ *
+ * Maintainers of the HotSpot VM must consider the following when modifying
+ * intrinsics.
+ *
+ * <ul>
+ * <li>When adding a new intrinsic, make sure that the corresponding method
+ * in the Java libraries is annotated with {@code @HotSpotIntrinsicCandidate}
+ * and that all possible call sequences that result in calling the intrinsic contain
+ * the checks omitted by the intrinsic (if any).</li>
+ * <li>When modifying an existing intrinsic, the Java libraries must be updated
+ * to match the semantics of the intrinsic and to execute all checks omitted
+ * by the intrinsic (if any).</li>
+ * </ul>
+ *
+ * Persons not directly involved with maintaining the Java libraries or the
+ * HotSpot VM can safely ignore the fact that a method is annotated with
+ * {@code @HotSpotIntrinsicCandidate}.
+ *
+ * The HotSpot VM defines (internally) a list of intrinsics. Not all intrinsic
+ * are available on all platforms supported by the HotSpot VM. Furthermore,
+ * the availability of an intrinsic on a given platform depends on the
+ * configuration of the HotSpot VM (e.g., the set of VM flags enabled).
+ * Therefore, annotating a method with {@code @HotSpotIntrinsicCandidate} does
+ * not guarantee that the marked method is intrinsified by the HotSpot VM.
+ *
+ * If the {@code CheckIntrinsics} VM flag is enabled, the HotSpot VM checks
+ * (when loading a class) that (1) all methods of that class that are also on
+ * the VM's list of intrinsics are annotated with {@code @HotSpotIntrinsicCandidate}
+ * and that (2) for all methods of that class annotated with
+ * {@code @HotSpotIntrinsicCandidate} there is an intrinsic in the list.
+ *
+ * @since 1.9
+ */
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HotSpotIntrinsicCandidate {
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/Archive.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/Archive.java	Thu Jul 09 22:46:18 2015 -0700
@@ -24,42 +24,95 @@
  */
 package jdk.internal.jimage;
 
+import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Path;
-import java.util.function.Consumer;
+import java.util.stream.Stream;
 
 /**
  * An Archive of all content, classes, resources, configuration files, and
  * other, for a module.
  */
 public interface Archive {
+
+    /**
+     * Entry is contained in an Archive
+     */
+    public abstract class Entry {
+
+        public static enum EntryType {
+
+            MODULE_NAME,
+            CLASS_OR_RESOURCE,
+            NATIVE_LIB,
+            NATIVE_CMD,
+            CONFIG,
+            SERVICE;
+        }
+
+        private final String name;
+        private final EntryType type;
+        private final Archive archive;
+        private final String path;
+
+        public Entry(Archive archive, String path, String name, EntryType type) {
+            this.archive = archive;
+            this.path = path;
+            this.name = name;
+            this.type = type;
+        }
+
+        public Archive archive() {
+            return archive;
+        }
+
+        public String path() {
+            return path;
+        }
+
+        public EntryType type() {
+            return type;
+        }
+
+        /**
+         * Returns the name of this entry.
+         */
+        public String name() {
+            return name;
+        }
+
+        @Override
+        public String toString() {
+            return "type " + type.name() + " path " + path;
+        }
+
+        /**
+         * Returns the number of uncompressed bytes for this entry.
+         */
+        public abstract long size();
+
+        public abstract InputStream stream() throws IOException;
+    }
+
     /**
      * The module name.
      */
     String moduleName();
 
     /**
-     * Visits all classes and resources.
+     * Stream of Entry.
+     * The stream of entries needs to be closed after use
+     * since it might cover lazy I/O based resources.
+     * So callers need to use a try-with-resources block.
      */
-    void visitResources(Consumer<Resource> consumer);
-
-    /**
-     * Visits all entries in the Archive.
-     */
-    void visitEntries(Consumer<Entry> consumer) ;
+    Stream<Entry> entries();
 
     /**
-     * An entries in the Archive.
+     * Open the archive
      */
-    interface Entry {
-        String getName();
-        InputStream getInputStream();
-        boolean isDirectory();
-    }
+    void open() throws IOException;
 
     /**
-     * A Consumer suitable for writing Entries from this Archive.
+     * Close the archive
      */
-    Consumer<Entry> defaultImageWriter(Path path, OutputStream out);
+    void close() throws IOException;
 }
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java	Thu Jul 09 22:46:18 2015 -0700
@@ -24,63 +24,88 @@
  */
 package jdk.internal.jimage;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.File;
+import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.IntBuffer;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import java.util.Comparator;
+import java.util.stream.IntStream;
 
 public class BasicImageReader {
     private final String imagePath;
-    private final PReader preader;
+    private final ImageSubstrate substrate;
     private final ByteOrder byteOrder;
-    private final ImageHeader header;
-    private final int indexSize;
-    private final IntBuffer redirectBuffer;
-    private final IntBuffer offsetsBuffer;
-    private final ByteBuffer locationsBuffer;
-    private final ByteBuffer stringsBuffer;
-    private final ImageStrings strings;
+    private final ImageStringsReader strings;
 
-    protected BasicImageReader(String imagePath, ByteOrder byteOrder) throws IOException {
+    protected BasicImageReader(String imagePath, ByteOrder byteOrder)
+            throws IOException {
         this.imagePath = imagePath;
-        this.preader = PReader.open(imagePath);
+        this.substrate = openImageSubstrate(imagePath, byteOrder);
         this.byteOrder = byteOrder;
-        this.header = ImageHeader.readFrom(byteOrder, getIntBuffer(0, ImageHeader.getHeaderSize()));
-        this.indexSize = header.getIndexSize();
-        this.redirectBuffer = getIntBuffer(header.getRedirectOffset(), header.getRedirectSize());
-        this.offsetsBuffer = getIntBuffer(header.getOffsetsOffset(), header.getOffsetsSize());
-        this.locationsBuffer = getByteBuffer(header.getLocationsOffset(), header.getLocationsSize());
-        this.stringsBuffer = getByteBuffer(header.getStringsOffset(), header.getStringsSize());
-        this.strings = new ImageStrings(new ImageStream(stringsBuffer));
+        this.strings = new ImageStringsReader(this);
     }
 
     protected BasicImageReader(String imagePath) throws IOException {
         this(imagePath, ByteOrder.nativeOrder());
     }
 
+    private static ImageSubstrate openImageSubstrate(String imagePath, ByteOrder byteOrder)
+            throws IOException {
+        ImageSubstrate substrate;
+
+        try {
+            substrate = ImageNativeSubstrate.openImage(imagePath, byteOrder);
+        } catch (UnsatisfiedLinkError ex) {
+            substrate = ImageJavaSubstrate.openImage(imagePath, byteOrder);
+        }
+
+        return substrate;
+    }
+
     public static BasicImageReader open(String imagePath) throws IOException {
         return new BasicImageReader(imagePath, ByteOrder.nativeOrder());
     }
 
+    public static void releaseByteBuffer(ByteBuffer buffer) {
+        ImageBufferCache.releaseBuffer(buffer);
+    }
+
+    public ByteOrder getByteOrder() {
+        return byteOrder;
+    }
+
     public String imagePath() {
         return imagePath;
     }
 
+    public String imagePathName() {
+        int slash = imagePath().lastIndexOf(File.separator);
+
+        if (slash != -1) {
+            return imagePath().substring(slash + 1);
+        }
+
+        return imagePath();
+    }
+
     public boolean isOpen() {
-        return preader.isOpen();
+        return true;
     }
 
     public void close() throws IOException {
-        preader.close();
+        substrate.close();
     }
 
-    public ImageHeader getHeader() {
-        return header;
+    public ImageHeader getHeader() throws IOException {
+        return ImageHeader.readFrom(
+                getIndexIntBuffer(0, ImageHeader.getHeaderSize()));
+    }
+
+    public ImageStringsReader getStrings() {
+        return strings;
     }
 
     public ImageLocation findLocation(String name) {
@@ -92,148 +117,147 @@
     }
 
     public synchronized ImageLocation findLocation(UTF8String name) {
-        int count = header.getLocationCount();
-        int hash = name.hashCode() % count;
-        int redirect = getRedirect(hash);
-
-        if (redirect == 0) {
-            return null;
-        }
-
-        int index;
-
-        if (redirect < 0) {
-            // If no collision.
-            index = -redirect - 1;
-        } else {
-            // If collision, recompute hash code.
-            index = name.hashCode(redirect) % count;
-        }
-
-        int offset = getOffset(index);
-
-        if (offset == 0) {
-            return null;
-        }
-
-        ImageLocation location = getLocation(offset);
-
-        return location.verify(name) ? location : null;
+        return substrate.findLocation(name, strings);
     }
 
     public String[] getEntryNames() {
-        return getEntryNames(true);
-    }
-
-    public String[] getEntryNames(boolean sorted) {
-        int count = header.getLocationCount();
-        List<String> list = new ArrayList<>();
-
-        for (int i = 0; i < count; i++) {
-            int offset = getOffset(i);
-
-            if (offset != 0) {
-                ImageLocation location = ImageLocation.readFrom(locationsBuffer, offset, strings);
-                list.add(location.getFullnameString());
-            }
-        }
-
-        String[] array = list.toArray(new String[0]);
-
-        if (sorted) {
-            Arrays.sort(array);
-        }
-
-        return array;
+        return IntStream.of(substrate.attributeOffsets())
+                        .filter(o -> o != 0)
+                        .mapToObj(o -> ImageLocation.readFrom(this, o).getFullNameString())
+                        .sorted()
+                        .toArray(String[]::new);
     }
 
     protected ImageLocation[] getAllLocations(boolean sorted) {
-        int count = header.getLocationCount();
-        List<ImageLocation> list = new ArrayList<>();
-
-        for (int i = 0; i < count; i++) {
-            int offset = getOffset(i);
-
-            if (offset != 0) {
-                ImageLocation location = ImageLocation.readFrom(locationsBuffer, offset, strings);
-                list.add(location);
-            }
-        }
-
-        ImageLocation[] array = list.toArray(new ImageLocation[0]);
-
-        if (sorted) {
-            Arrays.sort(array, (ImageLocation loc1, ImageLocation loc2) ->
-                    loc1.getFullnameString().compareTo(loc2.getFullnameString()));
-        }
-
-        return array;
+        return IntStream.of(substrate.attributeOffsets())
+                        .filter(o -> o != 0)
+                        .mapToObj(o -> ImageLocation.readFrom(this, o))
+                        .sorted(Comparator.comparing(ImageLocation::getFullNameString))
+                        .toArray(ImageLocation[]::new);
     }
 
-    private IntBuffer getIntBuffer(long offset, long size) throws IOException {
-        MappedByteBuffer buffer = preader.channel().map(FileChannel.MapMode.READ_ONLY, offset, size);
+    private IntBuffer getIndexIntBuffer(long offset, long size)
+            throws IOException {
+        ByteBuffer buffer = substrate.getIndexBuffer(offset, size);
         buffer.order(byteOrder);
 
         return buffer.asIntBuffer();
     }
 
-    private ByteBuffer getByteBuffer(long offset, long size) throws IOException {
-        MappedByteBuffer buffer = preader.channel().map(FileChannel.MapMode.READ_ONLY, offset, size);
-        // order is not copied into the readonly copy.
-        ByteBuffer readOnly = buffer.asReadOnlyBuffer();
-        readOnly.order(byteOrder);
-        return readOnly;
+    ImageLocation getLocation(int offset) {
+        return ImageLocation.readFrom(this, offset);
     }
 
-    private int getRedirect(int index) {
-        return redirectBuffer.get(index);
-    }
-
-    private int getOffset(int index) {
-        return offsetsBuffer.get(index);
-    }
-
-    private ImageLocation getLocation(int offset) {
-        return ImageLocation.readFrom(locationsBuffer, offset, strings);
+    public long[] getAttributes(int offset) {
+        return substrate.getAttributes(offset);
     }
 
     public String getString(int offset) {
-        return strings.get(offset).toString();
+        return getUTF8String(offset).toString();
+    }
+
+    public UTF8String getUTF8String(int offset) {
+        return new UTF8String(substrate.getStringBytes(offset));
+    }
+
+    private byte[] getBufferBytes(ByteBuffer buffer, long size) {
+        assert size < Integer.MAX_VALUE;
+        byte[] bytes = new byte[(int)size];
+        buffer.get(bytes);
+
+        return bytes;
+    }
+
+    private byte[] getBufferBytes(long offset, long size) {
+        ByteBuffer buffer = substrate.getDataBuffer(offset, size);
+
+        return getBufferBytes(buffer, size);
     }
 
-    public byte[] getResource(ImageLocation loc) throws IOException {
+    public byte[] getResource(ImageLocation loc) {
+        long offset = loc.getContentOffset();
         long compressedSize = loc.getCompressedSize();
+        long uncompressedSize = loc.getUncompressedSize();
         assert compressedSize < Integer.MAX_VALUE;
+        assert uncompressedSize < Integer.MAX_VALUE;
+
+        if (substrate.supportsDataBuffer() && compressedSize == 0) {
+            return getBufferBytes(offset, uncompressedSize);
+        }
+
+        ByteBuffer uncompressedBuffer = ImageBufferCache.getBuffer(uncompressedSize);
+        boolean isRead;
 
-        if (compressedSize == 0) {
-            return preader.read((int)loc.getUncompressedSize(),
-                                indexSize + loc.getContentOffset());
+        if (compressedSize != 0) {
+            ByteBuffer compressedBuffer = ImageBufferCache.getBuffer(compressedSize);
+            isRead = substrate.read(offset, compressedBuffer, compressedSize,
+                                          uncompressedBuffer, uncompressedSize);
+            ImageBufferCache.releaseBuffer(compressedBuffer);
         } else {
-            byte[] buf = preader.read((int)compressedSize,
-                                      indexSize + loc.getContentOffset());
-            return ImageFile.Compressor.decompress(buf);
+            isRead = substrate.read(offset, uncompressedBuffer, uncompressedSize);
         }
+
+        byte[] bytes = isRead ? getBufferBytes(uncompressedBuffer,
+                                               uncompressedSize) : null;
+
+        ImageBufferCache.releaseBuffer(uncompressedBuffer);
+
+        return bytes;
     }
 
-    public byte[] getResource(String name) throws IOException {
+    public byte[] getResource(String name) {
         ImageLocation location = findLocation(name);
 
         return location != null ? getResource(location) : null;
     }
 
-    public List<String> getNames(String name) throws IOException {
-        return getNames(getResource(name));
+    public ByteBuffer getResourceBuffer(ImageLocation loc) {
+        long offset = loc.getContentOffset();
+        long compressedSize = loc.getCompressedSize();
+        long uncompressedSize = loc.getUncompressedSize();
+        assert compressedSize < Integer.MAX_VALUE;
+        assert uncompressedSize < Integer.MAX_VALUE;
+
+        if (substrate.supportsDataBuffer() && compressedSize == 0) {
+            return substrate.getDataBuffer(offset, uncompressedSize);
+        }
+
+        ByteBuffer uncompressedBuffer = ImageBufferCache.getBuffer(uncompressedSize);
+        boolean isRead;
+
+        if (compressedSize != 0) {
+            ByteBuffer compressedBuffer = ImageBufferCache.getBuffer(compressedSize);
+            isRead = substrate.read(offset, compressedBuffer, compressedSize,
+                                          uncompressedBuffer, uncompressedSize);
+            ImageBufferCache.releaseBuffer(compressedBuffer);
+        } else {
+            isRead = substrate.read(offset, uncompressedBuffer, uncompressedSize);
+        }
+
+        if (isRead) {
+            return uncompressedBuffer;
+        } else {
+            ImageBufferCache.releaseBuffer(uncompressedBuffer);
+
+            return null;
+        }
     }
 
-    public List<String> getNames(byte[] bytes) {
-        IntBuffer buffer = ByteBuffer.wrap(bytes).asIntBuffer();
-        List<String> names = new ArrayList<>();
+    public ByteBuffer getResourceBuffer(String name) {
+        ImageLocation location = findLocation(name);
+
+        return location != null ? getResourceBuffer(location) : null;
+    }
 
-        while (buffer.hasRemaining()) {
-            int offset = buffer.get();
-            names.add(getString(offset));
-        }
+    public InputStream getResourceStream(ImageLocation loc) {
+        byte[] bytes = getResource(loc);
 
-        return names;
+        return new ByteArrayInputStream(bytes);
+    }
+
+    public InputStream getResourceStream(String name) {
+        ImageLocation location = findLocation(name);
+
+        return location != null ? getResourceStream(location) : null;
     }
 }
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageWriter.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageWriter.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,67 +25,30 @@
 
 package jdk.internal.jimage;
 
-import java.io.PrintStream;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 public final class BasicImageWriter {
+
+    public static final String IMAGE_EXT = ".jimage";
+    public static final String BOOT_NAME = "bootmodules";
+    public static final String BOOT_IMAGE_NAME = BOOT_NAME + IMAGE_EXT;
+
     private final static int RETRY_LIMIT = 1000;
 
     private ByteOrder byteOrder;
-    private ImageStrings strings;
-    private int count;
+    private ImageStringsWriter strings;
+    private int length;
     private int[] redirect;
-    private ImageLocation[] locations;
-    private List<ImageLocation> input;
+    private ImageLocationWriter[] locations;
+    private List<ImageLocationWriter> input;
     private ImageStream headerStream;
     private ImageStream redirectStream;
     private ImageStream locationOffsetStream;
     private ImageStream locationStream;
     private ImageStream allIndexStream;
 
-    static class ImageBucket implements Comparable<ImageBucket> {
-        final List<ImageLocation> list;
-
-        ImageBucket() {
-            this.list = new ArrayList<>();
-        }
-
-        void add(ImageLocation location) {
-            list.add(location);
-        }
-
-        int getSize() {
-            return list.size();
-        }
-
-        List<ImageLocation> getList() {
-            return list;
-        }
-
-        ImageLocation getFirst() {
-            assert !list.isEmpty() : "bucket should never be empty";
-            return list.get(0);
-        }
-
-        @Override
-        public int hashCode() {
-            return getFirst().hashCode();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            return this == obj;
-        }
-
-        @Override
-        public int compareTo(ImageBucket o) {
-            return o.getSize() - getSize();
-        }
-    }
-
     public BasicImageWriter() {
         this(ByteOrder.nativeOrder());
     }
@@ -93,7 +56,7 @@
     public BasicImageWriter(ByteOrder byteOrder) {
         this.byteOrder = byteOrder;
         this.input = new ArrayList<>();
-        this.strings = new ImageStrings();
+        this.strings = new ImageStringsWriter();
         this.headerStream = new ImageStream(byteOrder);
         this.redirectStream = new ImageStream(byteOrder);
         this.locationOffsetStream = new ImageStream(byteOrder);
@@ -101,6 +64,10 @@
         this.allIndexStream = new ImageStream(byteOrder);
     }
 
+    public ByteOrder getByteOrder() {
+        return byteOrder;
+    }
+
     public int addString(String string) {
         return addString(new UTF8String(string));
     }
@@ -109,104 +76,48 @@
         return strings.add(string);
     }
 
-    public void addLocation(String fullname, long contentOffset, long compressedSize, long uncompressedSize) {
-        ImageLocation location = ImageLocation.newLocation(new UTF8String(fullname), strings, contentOffset, compressedSize, uncompressedSize);
+    public String getString(int offset) {
+        UTF8String utf8 = strings.get(offset);
+        return utf8 != null? utf8.toString() : null;
+    }
+
+    public void addLocation(String fullname, long contentOffset,
+            long compressedSize, long uncompressedSize) {
+        ImageLocationWriter location =
+                ImageLocationWriter.newLocation(new UTF8String(fullname), strings,
+                        contentOffset, compressedSize, uncompressedSize);
         input.add(location);
-        count++;
+        length++;
+    }
+
+    ImageLocationWriter[] getLocations() {
+        return locations;
+    }
+
+    int getLocationsCount() {
+        return input.size();
     }
 
     private void generatePerfectHash() {
-        redo:
-        while(true) {
-            redirect = new int[count];
-            locations = new ImageLocation[count];
-
-            ImageBucket[] sorted = createBuckets();
-
-            int free = 0;
-
-            for (ImageBucket bucket : sorted) {
-                if (bucket.getSize() != 1) {
-                    if (!packCollidedEntries(bucket, count)) {
-                        count = (count + 1) | 1;
+        PerfectHashBuilder<ImageLocationWriter> builder =
+            new PerfectHashBuilder<>(
+                new PerfectHashBuilder.Entry<ImageLocationWriter>().getClass(),
+                new PerfectHashBuilder.Bucket<ImageLocationWriter>().getClass());
 
-                        continue redo;
-                    }
-                } else {
-                    for ( ; free < count && locations[free] != null; free++) {}
-                    assert free < count : "no free slots";
-                    locations[free] = bucket.getFirst();
-                    redirect[bucket.hashCode() % count] = -1 - free;
-                    free++;
-                }
-            }
-
-            break;
-        }
-    }
-
-    private ImageBucket[] createBuckets() {
-        ImageBucket[] buckets = new ImageBucket[count];
-
-        input.stream().forEach((location) -> {
-            int index = location.hashCode() % count;
-            ImageBucket bucket = buckets[index];
-
-            if (bucket == null) {
-                buckets[index] = bucket = new ImageBucket();
-            }
-
-            bucket.add(location);
+        input.forEach((location) -> {
+            builder.put(location.getFullName(), location);
         });
 
-        ImageBucket[] sorted = Arrays.asList(buckets).stream()
-                .filter((bucket) -> (bucket != null))
-                .sorted()
-                .toArray(ImageBucket[]::new);
-
-        return sorted;
-    }
-
-    private boolean packCollidedEntries(ImageBucket bucket, int count) {
-        List<Integer> undo = new ArrayList<>();
-        int base = UTF8String.HASH_MULTIPLIER + 1;
-
-        int retry = 0;
-
-        redo:
-        while (true) {
-            for (ImageLocation location : bucket.getList()) {
-                int index = location.hashCode(base) % count;
-
-                if (locations[index] != null) {
-                    undo.stream().forEach((i) -> {
-                        locations[i] = null;
-                    });
+        builder.generate();
 
-                    undo.clear();
-                    base++;
-
-                    if (base == 0) {
-                        base = 1;
-                    }
-
-                    if (++retry > RETRY_LIMIT) {
-                        return false;
-                    }
+        length = builder.getCount();
+        redirect = builder.getRedirect();
+        PerfectHashBuilder.Entry<ImageLocationWriter>[] order = builder.getOrder();
+        locations = new ImageLocationWriter[length];
 
-                    continue redo;
-                }
-
-                locations[index] = location;
-                undo.add(index);
-            }
-
-            redirect[bucket.hashCode() % count] = base;
-
-            break;
+        for (int i = 0; i < length; i++) {
+            locations[i] = order[i].getValue();
         }
-
-        return true;
     }
 
     private void prepareStringBytes() {
@@ -214,17 +125,17 @@
     }
 
     private void prepareRedirectBytes() {
-        for (int i = 0; i < count; i++) {
+        for (int i = 0; i < length; i++) {
             redirectStream.putInt(redirect[i]);
         }
     }
 
     private void prepareLocationBytes() {
         // Reserve location offset zero for empty locations
-        locationStream.put(ImageLocation.ATTRIBUTE_END << 3);
+        locationStream.put(ImageLocationWriter.ATTRIBUTE_END << 3);
 
-        for (int i = 0; i < count; i++) {
-            ImageLocation location = locations[i];
+        for (int i = 0; i < length; i++) {
+            ImageLocationWriter location = locations[i];
 
             if (location != null) {
                 location.writeTo(locationStream);
@@ -235,14 +146,16 @@
     }
 
     private void prepareOffsetBytes() {
-        for (int i = 0; i < count; i++) {
-            ImageLocation location = locations[i];
-            locationOffsetStream.putInt(location != null ? location.getLocationOffset() : 0);
+        for (int i = 0; i < length; i++) {
+            ImageLocationWriter location = locations[i];
+            int offset = location != null ? location.getLocationOffset() : 0;
+            locationOffsetStream.putInt(offset);
         }
     }
 
     private void prepareHeaderBytes() {
-        ImageHeader header = new ImageHeader(count, locationStream.getSize(), strings.getSize());
+        ImageHeader header = new ImageHeader(input.size(), length,
+                locationStream.getSize(), strings.getSize());
         header.writeTo(headerStream);
     }
 
@@ -268,33 +181,15 @@
         return allIndexStream.toArray();
     }
 
-    ImageLocation find(UTF8String key) {
-        int index = key.hashCode() % count;
-        index = redirect[index];
+    ImageLocationWriter find(UTF8String key) {
+        int index = redirect[key.hashCode() % length];
 
         if (index < 0) {
             index = -index - 1;
-            ImageLocation location = locations[index];
-
-            return location;
         } else {
-            index = key.hashCode(index) % count;
-            ImageLocation location = locations[index];
-
-            return location;
+            index = key.hashCode(index) % length;
         }
-    }
 
-    public void statistics() {
-        getBytes();
-        PrintStream out = System.out;
-        out.println("Count: " + count);
-        out.println("Header bytes size: " + headerStream.getSize());
-        out.println("Redirect bytes size: " + redirectStream.getSize());
-        out.println("Offset bytes size: " + locationOffsetStream.getSize());
-        out.println("Location bytes size: " + locationStream.getSize());
-        out.println("String count: " + strings.getCount());
-        out.println("String bytes size: " + strings.getSize());
-        out.println("Total bytes size: " + allIndexStream.getSize());
+        return locations[index];
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ExternalFilesWriter.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,105 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jimage;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.function.Consumer;
+import jdk.internal.jimage.Archive.Entry;
+
+/**
+ * A Consumer suitable for processing non resources Archive Entry and writing it to the
+ * appropriate location.
+ */
+class ExternalFilesWriter implements Consumer<Entry> {
+    private final Path root;
+
+    ExternalFilesWriter(Path root) {
+        this.root = root;
+    }
+
+    @Override
+    public void accept(Entry entry) {
+        String name = entry.path();
+        try {
+            String filename = entry.path();
+            try (InputStream in = entry.stream()) {
+                switch (entry.type()) {
+                    case NATIVE_LIB:
+                        writeEntry(in, destFile(nativeDir(filename), filename));
+                        break;
+                    case NATIVE_CMD:
+                        Path path = destFile("bin", filename);
+                        writeEntry(in, path);
+                        path.toFile().setExecutable(true);
+                        break;
+                    case CONFIG:
+                        writeEntry(in, destFile("conf", filename));
+                        break;
+                    case MODULE_NAME:
+                        // skip
+                        break;
+                    case SERVICE:
+                        //throw new UnsupportedOperationException(name + " in " + zipfile.toString()); //TODO
+                        throw new UnsupportedOperationException(name + " in " + name);
+                    default:
+                        //throw new InternalError("unexpected entry: " + name + " " + zipfile.toString()); //TODO
+                        throw new InternalError("unexpected entry: " + name + " " + name);
+                }
+            }
+        } catch (FileAlreadyExistsException x) {
+            System.err.println("File already exists (skipped) " + name);
+        } catch (IOException x) {
+            throw new UncheckedIOException(x);
+        }
+    }
+
+    private Path destFile(String dir, String filename) {
+        return root.resolve(dir).resolve(filename);
+    }
+
+    private void writeEntry(InputStream in, Path dstFile) throws IOException {
+        Files.createDirectories(dstFile.getParent());
+        Files.copy(in, dstFile);
+    }
+
+    private static String nativeDir(String filename) {
+        if (System.getProperty("os.name").startsWith("Windows")) {
+            if (filename.endsWith(".dll") || filename.endsWith(".diz")
+                || filename.endsWith(".pdb") || filename.endsWith(".map")) {
+                return "bin";
+            } else {
+                return "lib";
+            }
+        } else {
+            return "lib";
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageBufferCache.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,123 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+class ImageBufferCache {
+    private static final int MAX_FREE_BUFFERS = 3;
+    private static final int LARGE_BUFFER = 0x10000;
+    private static final ThreadLocal<ArrayList<ImageBufferCache>>
+            threadLocal = new ThreadLocal<>();
+
+    private final ByteBuffer buffer;
+    private boolean isUsed;
+
+    static ByteBuffer getBuffer(long size) {
+        assert size < Integer.MAX_VALUE;
+        ByteBuffer buffer = null;
+
+        if (size > LARGE_BUFFER) {
+            buffer = ByteBuffer.allocateDirect((int)((size + 0xFFF) & ~0xFFF));
+        } else {
+            ArrayList<ImageBufferCache> buffers = threadLocal.get();
+
+            if (buffers == null) {
+                buffers = new ArrayList<>(MAX_FREE_BUFFERS);
+                threadLocal.set(buffers);
+            }
+
+            int i = 0, j = buffers.size();
+            for (ImageBufferCache imageBuffer : buffers) {
+                if (size <= imageBuffer.capacity()) {
+                    j = i;
+
+                    if (!imageBuffer.isUsed) {
+                        imageBuffer.isUsed = true;
+                        buffer = imageBuffer.buffer;
+
+                        break;
+                    }
+                } else {
+                    break;
+                }
+
+                i++;
+            }
+
+            if (buffer == null) {
+                ImageBufferCache imageBuffer = new ImageBufferCache((int)size);
+                buffers.add(j, imageBuffer);
+                buffer = imageBuffer.buffer;
+            }
+        }
+
+        buffer.rewind();
+        buffer.limit((int)size);
+
+        return buffer;
+    }
+
+    static void releaseBuffer(ByteBuffer buffer) {
+        ArrayList<ImageBufferCache> buffers = threadLocal.get();
+
+        if (buffers == null ) {
+            return;
+        }
+
+        if (buffer.capacity() > LARGE_BUFFER) {
+            return;
+        }
+
+        int i = 0, j = buffers.size();
+        for (ImageBufferCache imageBuffer : buffers) {
+            if (!imageBuffer.isUsed) {
+                j = Math.min(j, i);
+            }
+
+            if (imageBuffer.buffer == buffer) {
+                imageBuffer.isUsed = false;
+                j = Math.min(j, i);
+
+                break;
+            }
+        }
+
+        if (buffers.size() > MAX_FREE_BUFFERS && j != buffers.size()) {
+            buffers.remove(j);
+        }
+    }
+
+    private ImageBufferCache(int needed) {
+        this.buffer = ByteBuffer.allocateDirect((needed + 0xFFF) & ~0xFFF);
+        this.isUsed = true;
+        this.buffer.limit(needed);
+    }
+
+    private long capacity() {
+        return buffer.capacity();
+    }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageFile.java	Wed Jul 05 20:41:30 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,288 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package jdk.internal.jimage;
-
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteOrder;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.zip.DataFormatException;
-import java.util.zip.Deflater;
-import java.util.zip.Inflater;
-import jdk.internal.jimage.ImageModules.Loader;
-import jdk.internal.jimage.ImageModules.ModuleIndex;
-
-/**
- * An image (native endian.)
- * <pre>{@code
- * {
- *   u4 magic;
- *   u2 major_version;
- *   u2 minor_version;
- *   u4 location_count;
- *   u4 location_attributes_size;
- *   u4 strings_size;
- *   u4 redirect[location_count];
- *   u4 offsets[location_count];
- *   u1 location_attributes[location_attributes_size];
- *   u1 strings[strings_size];
- *   u1 content[if !EOF];
- * }
- * }</pre>
- */
-public final class ImageFile {
-    private static final String JAVA_BASE = "java.base";
-    private static final String IMAGE_EXT = ".jimage";
-    private static final String JAR_EXT = ".jar";
-    private final Path root;
-    private final Path mdir;
-    private final Map<String, List<Resource>> resourcesForModule = new HashMap<>();
-
-    private ImageFile(Path path) {
-        this.root = path;
-        this.mdir = root.resolve(path.getFileSystem().getPath("lib", "modules"));
-    }
-
-    public static ImageFile open(Path path) throws IOException {
-        ImageFile lib = new ImageFile(path);
-        return lib.open();
-    }
-
-    private ImageFile open() throws IOException {
-        Path path = mdir.resolve("bootmodules" + IMAGE_EXT);
-
-        ImageReader reader = new ImageReader(path.toString());
-        ImageHeader header = reader.getHeader();
-
-        if (header.getMagic() != ImageHeader.MAGIC) {
-            if (header.getMagic() == ImageHeader.BADMAGIC) {
-                throw new IOException(path + ": Image may be not be native endian");
-            } else {
-                throw new IOException(path + ": Invalid magic number");
-            }
-        }
-
-        if (header.getMajorVersion() > ImageHeader.MAJOR_VERSION ||
-            (header.getMajorVersion() == ImageHeader.MAJOR_VERSION &&
-             header.getMinorVersion() > ImageHeader.MINOR_VERSION)) {
-            throw new IOException("invalid version number");
-        }
-
-        return this;
-    }
-
-    public static ImageFile create(Path output,
-                                   Set<Archive> archives,
-                                   ImageModules modules)
-        throws IOException
-    {
-        return ImageFile.create(output, archives, modules, ByteOrder.nativeOrder());
-    }
-
-    public static ImageFile create(Path output,
-                                   Set<Archive> archives,
-                                   ImageModules modules,
-                                   ByteOrder byteOrder)
-        throws IOException
-    {
-        ImageFile lib = new ImageFile(output);
-        // get all resources
-        lib.readModuleEntries(modules, archives);
-        // write to modular image
-        lib.writeImage(modules, archives, byteOrder);
-        return lib;
-    }
-
-    private void writeImage(ImageModules modules,
-                            Set<Archive> archives,
-                            ByteOrder byteOrder)
-        throws IOException
-    {
-        // name to Archive file
-        Map<String, Archive> nameToArchive =
-            archives.stream()
-                  .collect(Collectors.toMap(Archive::moduleName, Function.identity()));
-
-        Files.createDirectories(mdir);
-        for (Loader l : Loader.values()) {
-            Set<String> mods = modules.getModules(l);
-
-            try (OutputStream fos = Files.newOutputStream(mdir.resolve(l.getName() + IMAGE_EXT));
-                    BufferedOutputStream bos = new BufferedOutputStream(fos);
-                    DataOutputStream out = new DataOutputStream(bos)) {
-                // store index in addition of the class loader map for boot loader
-                BasicImageWriter writer = new BasicImageWriter(byteOrder);
-                Set<String> duplicates = new HashSet<>();
-
-                // build package map for modules and add as resources
-                ModuleIndex mindex = modules.buildModuleIndex(l, writer);
-                long offset = mindex.size();
-
-                // the order of traversing the resources and the order of
-                // the module content being written must be the same
-                for (String mn : mods) {
-                    for (Resource res : resourcesForModule.get(mn)) {
-                        String path = res.name();
-                        long uncompressedSize = res.size();
-                        long compressedSize = res.csize();
-                        long onFileSize = compressedSize != 0 ? compressedSize : uncompressedSize;
-
-                        if (duplicates.contains(path)) {
-                            System.err.format("duplicate resource \"%s\", skipping%n", path);
-                            // TODO Need to hang bytes on resource and write from resource not zip.
-                            // Skipping resource throws off writing from zip.
-                            offset += onFileSize;
-                            continue;
-                        }
-                        duplicates.add(path);
-                        writer.addLocation(path, offset, compressedSize, uncompressedSize);
-                        offset += onFileSize;
-                    }
-                }
-
-                // write header and indices
-                byte[] bytes = writer.getBytes();
-                out.write(bytes, 0, bytes.length);
-
-                // write module table and packages
-                mindex.writeTo(out);
-
-                // write module content
-                for (String mn : mods) {
-                    writeModule(nameToArchive.get(mn), out);
-                }
-            }
-        }
-    }
-
-    private void readModuleEntries(ImageModules modules,
-                                   Set<Archive> archives)
-        throws IOException
-    {
-        for (Archive archive : archives) {
-            List<Resource> res = new ArrayList<>();
-            archive.visitResources(x-> res.add(x));
-
-            String mn = archive.moduleName();
-            resourcesForModule.put(mn, res);
-
-            Set<String> pkgs = res.stream().map(Resource::name)
-                    .filter(n -> n.endsWith(".class"))
-                    .map(this::toPackage)
-                    .distinct()
-                    .collect(Collectors.toSet());
-            modules.setPackages(mn, pkgs);
-        }
-    }
-
-    private String toPackage(String name) {
-        int index = name.lastIndexOf('/');
-        if (index > 0) {
-            return name.substring(0, index).replace('/', '.');
-        } else {
-            // ## unnamed package
-            System.err.format("Warning: %s in unnamed package%n", name);
-            return "";
-        }
-    }
-
-    private void writeModule(Archive archive,
-                             OutputStream out)
-        throws IOException
-    {
-          Consumer<Archive.Entry> consumer = archive.defaultImageWriter(root, out);
-          archive.visitEntries(consumer);
-    }
-
-
-    static class Compressor {
-        public static byte[] compress(byte[] bytesIn) {
-            Deflater deflater = new Deflater();
-            deflater.setInput(bytesIn);
-            ByteArrayOutputStream stream = new ByteArrayOutputStream(bytesIn.length);
-            byte[] buffer = new byte[1024];
-
-            deflater.finish();
-            while (!deflater.finished()) {
-                int count = deflater.deflate(buffer);
-                stream.write(buffer, 0, count);
-            }
-
-            try {
-                stream.close();
-            } catch (IOException ex) {
-                return bytesIn;
-            }
-
-            byte[] bytesOut = stream.toByteArray();
-            deflater.end();
-
-            return bytesOut;
-        }
-
-        public static byte[] decompress(byte[] bytesIn) {
-            Inflater inflater = new Inflater();
-            inflater.setInput(bytesIn);
-            ByteArrayOutputStream stream = new ByteArrayOutputStream(bytesIn.length);
-            byte[] buffer = new byte[1024];
-
-            while (!inflater.finished()) {
-                int count;
-
-                try {
-                    count = inflater.inflate(buffer);
-                } catch (DataFormatException ex) {
-                    return null;
-                }
-
-                stream.write(buffer, 0, count);
-            }
-
-            try {
-                stream.close();
-            } catch (IOException ex) {
-                return null;
-            }
-
-            byte[] bytesOut = stream.toByteArray();
-            inflater.end();
-
-            return bytesOut;
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageFileCreator.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,355 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import jdk.internal.jimage.Archive.Entry;
+import jdk.internal.jimage.Archive.Entry.EntryType;
+import static jdk.internal.jimage.BasicImageWriter.BOOT_NAME;
+import static jdk.internal.jimage.BasicImageWriter.IMAGE_EXT;
+
+/**
+ * An image (native endian.)
+ * <pre>{@code
+ * {
+ *   u4 magic;
+ *   u2 major_version;
+ *   u2 minor_version;
+ *   u4 resource_count;
+ *   u4 table_length;
+ *   u4 location_attributes_size;
+ *   u4 strings_size;
+ *   u4 redirect[table_length];
+ *   u4 offsets[table_length];
+ *   u1 location_attributes[location_attributes_size];
+ *   u1 strings[strings_size];
+ *   u1 content[if !EOF];
+ * }
+ * }</pre>
+ */
+public final class ImageFileCreator {
+    private final Path root;
+    private final Path mdir;
+    private final Map<String, List<Entry>> entriesForModule = new HashMap<>();
+    private ImageFileCreator(Path path) {
+        this.root = path;
+        this.mdir = root.resolve(path.getFileSystem().getPath("lib", "modules"));
+    }
+
+    public static ImageFileCreator create(Path output,
+            Set<Archive> archives)
+            throws IOException {
+        return create(output, BOOT_NAME, archives, ByteOrder.nativeOrder());
+    }
+
+    public static ImageFileCreator create(Path output,
+            Set<Archive> archives,
+            ByteOrder byteOrder)
+            throws IOException {
+        return create(output, BOOT_NAME, archives, byteOrder);
+    }
+
+    public static ImageFileCreator create(Path output,
+                                   String fileName,
+                                   Set<Archive> archives,
+                                   ByteOrder byteOrder)
+        throws IOException
+    {
+        ImageFileCreator image = new ImageFileCreator(output);
+        // get all entries
+        Map<String, Set<String>> modulePackagesMap = new HashMap<>();
+        image.readAllEntries(modulePackagesMap, archives);
+        // write to modular image
+        image.writeImage(fileName, modulePackagesMap, archives, byteOrder);
+        return image;
+    }
+
+    private void readAllEntries(Map<String, Set<String>> modulePackagesMap,
+                                  Set<Archive> archives) {
+        archives.stream().forEach((archive) -> {
+            Map<Boolean, List<Entry>> es;
+            try(Stream<Entry> entries = archive.entries()) {
+                es = entries.collect(Collectors.partitioningBy(n -> n.type()
+                        == EntryType.CLASS_OR_RESOURCE));
+            }
+            String mn = archive.moduleName();
+            List<Entry> all = new ArrayList<>();
+            all.addAll(es.get(false));
+            all.addAll(es.get(true));
+            entriesForModule.put(mn, all);
+            // Extract package names
+            Set<String> pkgs = es.get(true).stream().map(Entry::name)
+                    .filter(n -> isClassPackage(n))
+                    .map(ImageFileCreator::toPackage)
+                    .collect(Collectors.toSet());
+            modulePackagesMap.put(mn, pkgs);
+        });
+    }
+
+    public static boolean isClassPackage(String path) {
+        return path.endsWith(".class");
+    }
+
+    public static boolean isResourcePackage(String path) {
+        path = path.substring(1);
+        path = path.substring(path.indexOf("/")+1);
+        return !path.startsWith("META-INF/");
+    }
+
+    public static void recreateJimage(Path jimageFile,
+            Set<Archive> archives,
+            Map<String, Set<String>> modulePackages)
+            throws IOException {
+        Map<String, List<Entry>> entriesForModule
+                = archives.stream().collect(Collectors.toMap(
+                                Archive::moduleName,
+                                a -> {
+                                    try(Stream<Entry> entries = a.entries()) {
+                                        return entries.collect(Collectors.toList());
+                                    }
+                                }));
+        Map<String, Archive> nameToArchive
+                = archives.stream()
+                .collect(Collectors.toMap(Archive::moduleName, Function.identity()));
+        ByteOrder order = ByteOrder.nativeOrder();
+        ResourcePoolImpl resources = createResources(modulePackages, nameToArchive,
+                (Entry t) -> {
+            throw new UnsupportedOperationException("Not supported, no external file "
+                    + "in a jimage file");
+        }, entriesForModule, order);
+        String fileName = jimageFile.getRoot().toString();
+        generateJImage(jimageFile, fileName, resources, order);
+    }
+
+    private void writeImage(String fileName,
+            Map<String, Set<String>> modulePackagesMap,
+            Set<Archive> archives,
+            ByteOrder byteOrder)
+            throws IOException {
+        Files.createDirectories(mdir);
+        ExternalFilesWriter filesWriter = new ExternalFilesWriter(root);
+        // name to Archive file
+        Map<String, Archive> nameToArchive
+                = archives.stream()
+                .collect(Collectors.toMap(Archive::moduleName, Function.identity()));
+        ResourcePoolImpl resources = createResources(modulePackagesMap,
+                nameToArchive, filesWriter,
+                entriesForModule, byteOrder);
+        generateJImage(mdir.resolve(fileName + IMAGE_EXT), fileName, resources,
+                byteOrder);
+    }
+
+    private static void generateJImage(Path img,
+            String fileName,
+            ResourcePoolImpl resources,
+            ByteOrder byteOrder
+    ) throws IOException {
+        BasicImageWriter writer = new BasicImageWriter(byteOrder);
+
+        Map<String, Set<String>> modulePackagesMap = resources.getModulePackages();
+
+        try (OutputStream fos = Files.newOutputStream(img);
+                BufferedOutputStream bos = new BufferedOutputStream(fos);
+                DataOutputStream out = new DataOutputStream(bos)) {
+            Set<String> duplicates = new HashSet<>();
+            ImageModuleDataWriter moduleData =
+            ImageModuleDataWriter.buildModuleData(writer, modulePackagesMap);
+            moduleData.addLocation(fileName, writer);
+            long offset = moduleData.size();
+
+            List<ResourcePool.Resource> content = new ArrayList<>();
+            List<String> paths = new ArrayList<>();
+                 // the order of traversing the resources and the order of
+            // the module content being written must be the same
+            for (ResourcePool.Resource res : resources.getResources()) {
+                String path = res.getPath();
+                int index = path.indexOf("/META-INF/");
+                if (index != -1) {
+                    path = path.substring(index + 1);
+                }
+
+                content.add(res);
+                long uncompressedSize = res.getLength();
+                long compressedSize = 0;
+                if (res instanceof ResourcePool.CompressedResource) {
+                    ResourcePool.CompressedResource comp =
+                            (ResourcePool.CompressedResource) res;
+                    compressedSize = res.getLength();
+                    uncompressedSize = comp.getUncompressedSize();
+                }
+                long onFileSize = res.getLength();
+
+                if (duplicates.contains(path)) {
+                    System.err.format("duplicate resource \"%s\", skipping%n",
+                            path);
+                     // TODO Need to hang bytes on resource and write
+                    // from resource not zip.
+                    // Skipping resource throws off writing from zip.
+                    offset += onFileSize;
+                    continue;
+                }
+                duplicates.add(path);
+                writer.addLocation(path, offset, compressedSize, uncompressedSize);
+                paths.add(path);
+                offset += onFileSize;
+            }
+
+            ImageResourcesTree tree = new ImageResourcesTree(offset, writer, paths);
+
+            // write header and indices
+            byte[] bytes = writer.getBytes();
+            out.write(bytes, 0, bytes.length);
+
+            // write module meta data
+            moduleData.writeTo(out);
+
+            // write module content
+            for(ResourcePool.Resource res : content) {
+                byte[] buf = res.getByteArray();
+                out.write(buf, 0, buf.length);
+            }
+
+            tree.addContent(out);
+        }
+    }
+
+    private static ResourcePoolImpl createResources(Map<String, Set<String>> modulePackagesMap,
+            Map<String, Archive> nameToArchive,
+            Consumer<Entry> externalFileHandler,
+            Map<String, List<Entry>> entriesForModule,
+            ByteOrder byteOrder) throws IOException {
+        ResourcePoolImpl resources = new ResourcePoolImpl(byteOrder);
+        Set<String> mods = modulePackagesMap.keySet();
+        for (String mn : mods) {
+            for (Entry entry : entriesForModule.get(mn)) {
+                String path = entry.name();
+                if (entry.type() == EntryType.CLASS_OR_RESOURCE) {
+                    if (!entry.path().endsWith(BOOT_NAME)) {
+                        try (InputStream stream = entry.stream()) {
+                            byte[] bytes = readAllBytes(stream);
+                            path = "/" + mn + "/" + path;
+                            try {
+                                resources.addResource(new ResourcePool.Resource(path,
+                                        ByteBuffer.wrap(bytes)));
+                            } catch (Exception ex) {
+                                throw new IOException(ex);
+                            }
+                        }
+                    }
+                } else {
+                    externalFileHandler.accept(entry);
+                }
+            }
+            // Done with this archive, close it.
+            Archive archive = nameToArchive.get(mn);
+            archive.close();
+        }
+        return resources;
+    }
+
+    private static final int BUF_SIZE = 8192;
+
+    private static byte[] readAllBytes(InputStream is) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        byte[] buf = new byte[BUF_SIZE];
+        while (true) {
+            int n = is.read(buf);
+            if (n < 0) {
+                break;
+            }
+            baos.write(buf, 0, n);
+        }
+        return baos.toByteArray();
+    }
+
+    /**
+     * Helper method that splits a Resource path onto 3 items: module, parent
+     * and resource name.
+     *
+     * @param path
+     * @return An array containing module, parent and name.
+     */
+    public static String[] splitPath(String path) {
+        Objects.requireNonNull(path);
+        String noRoot = path.substring(1);
+        int pkgStart = noRoot.indexOf("/");
+        String module = noRoot.substring(0, pkgStart);
+        List<String> result = new ArrayList<>();
+        result.add(module);
+        String pkg = noRoot.substring(pkgStart + 1);
+        String resName;
+        int pkgEnd = pkg.lastIndexOf("/");
+        if (pkgEnd == -1) { // No package.
+            resName = pkg;
+        } else {
+            resName = pkg.substring(pkgEnd + 1);
+        }
+
+        pkg = toPackage(pkg, false);
+        result.add(pkg);
+        result.add(resName);
+
+        String[] array = new String[result.size()];
+        return result.toArray(array);
+    }
+
+    private static String toPackage(String name) {
+        String pkg = toPackage(name, true);
+        return pkg;
+    }
+
+    private static String toPackage(String name, boolean log) {
+        int index = name.lastIndexOf('/');
+        if (index > 0) {
+            return name.substring(0, index).replace('/', '.');
+        } else {
+            // ## unnamed package
+            if (log) {
+                System.err.format("Warning: %s in unnamed package%n", name);
+            }
+            return "";
+        }
+    }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageHeader.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageHeader.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,67 +25,76 @@
 
 package jdk.internal.jimage;
 
-import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 
 public final class ImageHeader {
     public static final int MAGIC = 0xCAFEDADA;
     public static final int BADMAGIC = 0xDADAFECA;
-    public static final short MAJOR_VERSION = 0;
-    public static final short MINOR_VERSION = 1;
+    public static final int MAJOR_VERSION = 1;
+    public static final int MINOR_VERSION = 0;
 
     private final int magic;
-    private final short majorVersion;
-    private final short minorVersion;
-    private final int locationCount;
+    private final int majorVersion;
+    private final int minorVersion;
+    private final int flags;
+    private final int resourceCount;
+    private final int tableLength;
     private final int locationsSize;
     private final int stringsSize;
 
-    ImageHeader(int locationCount, int locationsSize, int stringsSize) {
-        this(MAGIC, MAJOR_VERSION, MINOR_VERSION, locationCount, locationsSize, stringsSize);
+    public ImageHeader(int resourceCount, int tableCount,
+            int locationsSize, int stringsSize) {
+        this(MAGIC, MAJOR_VERSION, MINOR_VERSION, 0, resourceCount,
+                tableCount, locationsSize, stringsSize);
     }
 
-    ImageHeader(int magic, short majorVersion, short minorVersion, int locationCount,
-                int locationsSize, int stringsSize)
+    public ImageHeader(int magic, int majorVersion, int minorVersion,
+                int flags, int resourceCount,
+                int tableLength, int locationsSize, int stringsSize)
     {
         this.magic = magic;
         this.majorVersion = majorVersion;
         this.minorVersion = minorVersion;
-        this.locationCount = locationCount;
+        this.flags = flags;
+        this.resourceCount = resourceCount;
+        this.tableLength = tableLength;
         this.locationsSize = locationsSize;
         this.stringsSize = stringsSize;
     }
 
-    static int getHeaderSize() {
-       return 4 +
-              2 + 2 +
-              4 +
-              4 +
-              4;
+    public static int getHeaderSize() {
+       return 7 * 4;
     }
 
-    static ImageHeader readFrom(ByteOrder byteOrder, IntBuffer buffer) {
+    static ImageHeader readFrom(IntBuffer buffer) {
         int magic = buffer.get(0);
         int version = buffer.get(1);
-        short majorVersion = (short)(byteOrder == ByteOrder.BIG_ENDIAN ?
-            version >>> 16 : (version & 0xFFFF));
-        short minorVersion = (short)(byteOrder == ByteOrder.BIG_ENDIAN ?
-            (version & 0xFFFF) : version >>> 16);
-        int locationCount = buffer.get(2);
-        int locationsSize = buffer.get(3);
-        int stringsSize = buffer.get(4);
+        int majorVersion = version >>> 16;
+        int minorVersion = version & 0xFFFF;
+        int flags = buffer.get(2);
+        int resourceCount = buffer.get(3);
+        int tableLength = buffer.get(4);
+        int locationsSize = buffer.get(5);
+        int stringsSize = buffer.get(6);
 
-        return new ImageHeader(magic, majorVersion, minorVersion, locationCount,
-                               locationsSize, stringsSize);
+        return new ImageHeader(magic, majorVersion, minorVersion, flags,
+            resourceCount, tableLength, locationsSize, stringsSize);
     }
 
     void writeTo(ImageStream stream) {
-        stream.putInt(magic);
-        stream.putShort(majorVersion);
-        stream.putShort(minorVersion);
-        stream.putInt(locationCount);
-        stream.putInt(locationsSize);
-        stream.putInt(stringsSize);
+        stream.ensure(getHeaderSize());
+        writeTo(stream.getBuffer());
+    }
+
+    public void writeTo(ByteBuffer buffer) {
+        buffer.putInt(magic);
+        buffer.putInt(majorVersion << 16 | minorVersion);
+        buffer.putInt(flags);
+        buffer.putInt(resourceCount);
+        buffer.putInt(tableLength);
+        buffer.putInt(locationsSize);
+        buffer.putInt(stringsSize);
     }
 
     public int getMagic() {
@@ -100,16 +109,24 @@
         return minorVersion;
     }
 
-    public int getLocationCount() {
-        return locationCount;
+    public int getFlags() {
+        return flags;
+    }
+
+    public int getResourceCount() {
+        return resourceCount;
+    }
+
+    public int getTableLength() {
+        return tableLength;
     }
 
     public int getRedirectSize() {
-        return locationCount* 4;
+        return tableLength * 4;
     }
 
     public int getOffsetsSize() {
-        return locationCount* 4;
+        return tableLength * 4;
     }
 
     public int getLocationsSize() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageJavaSubstrate.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,242 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Paths;
+import static java.nio.file.StandardOpenOption.READ;
+import jdk.internal.jimage.decompressor.Decompressor;
+
+final class ImageJavaSubstrate implements ImageSubstrate {
+
+    private final String imagePath;
+    private final ByteOrder byteOrder;
+    private final FileChannel channel;
+    private final ImageHeader header;
+    private final long indexSize;
+    private final int[] redirect;
+    private final int[] offsets;
+    private final byte[] locations;
+    private final byte[] strings;
+
+    private final Decompressor decompressor = new Decompressor();
+
+  private ImageJavaSubstrate(String imagePath, ByteOrder byteOrder)
+          throws IOException {
+        this.imagePath = imagePath;
+        this.byteOrder = byteOrder;
+        channel = FileChannel.open(Paths.get(imagePath), READ);
+
+        int headerSize = ImageHeader.getHeaderSize();
+        ByteBuffer buffer = getIndexBuffer(0, headerSize);
+        header = ImageHeader.readFrom(buffer.asIntBuffer());
+
+        if (header.getMagic() != ImageHeader.MAGIC ||
+            header.getMajorVersion() != ImageHeader.MAJOR_VERSION ||
+            header.getMinorVersion() != ImageHeader.MINOR_VERSION) {
+            throw new IOException("Image not found \"" + imagePath + "\"");
+        }
+
+        indexSize = header.getIndexSize();
+
+        redirect = readIntegers(header.getRedirectOffset(),
+                                header.getRedirectSize());
+        offsets = readIntegers(header.getOffsetsOffset(),
+                               header.getOffsetsSize());
+        locations = readBytes(header.getLocationsOffset(),
+                              header.getLocationsSize());
+        strings = readBytes(header.getStringsOffset(),
+                            header.getStringsSize());
+    }
+
+    static ImageSubstrate openImage(String imagePath, ByteOrder byteOrder)
+            throws IOException {
+        return new ImageJavaSubstrate(imagePath, byteOrder);
+    }
+
+    @Override
+    public void close() {
+        try {
+            channel.close();
+        } catch (IOException ex) {
+            // Mostly harmless
+        }
+    }
+
+    @Override
+    public boolean supportsDataBuffer() {
+        return false;
+    }
+
+    private int[] readIntegers(long offset, long size) {
+        assert size < Integer.MAX_VALUE;
+        IntBuffer buffer = readBuffer(offset, size).asIntBuffer();
+        int[] integers = new int[(int)size / 4];
+        buffer.get(integers);
+
+        return integers;
+    }
+
+    private byte[] readBytes(long offset, long size) {
+        assert size < Integer.MAX_VALUE;
+        ByteBuffer buffer = readBuffer(offset, size);
+        byte[] bytes = new byte[(int)size];
+        buffer.get(bytes);
+
+        return bytes;
+    }
+
+    private ByteBuffer readBuffer(long offset, long size) {
+        assert size < Integer.MAX_VALUE;
+        ByteBuffer buffer = ByteBuffer.allocate((int)size);
+        buffer.order(byteOrder);
+
+        if (!readBuffer(buffer, offset, size)) {
+            return null;
+        }
+
+        return buffer;
+    }
+
+    private boolean readBuffer(ByteBuffer buffer, long offset, long size) {
+        assert size < Integer.MAX_VALUE;
+        assert buffer.limit() == size;
+        int read = 0;
+
+        try {
+            read = channel.read(buffer, offset);
+            buffer.rewind();
+        } catch (IOException ex) {
+            // fall thru
+        }
+
+        return read == size;
+    }
+
+    @Override
+    public ByteBuffer getIndexBuffer(long offset, long size) {
+        assert size < Integer.MAX_VALUE;
+        return readBuffer(offset, size);
+    }
+
+    @Override
+    public ByteBuffer getDataBuffer(long offset, long size) {
+        assert size < Integer.MAX_VALUE;
+        return getIndexBuffer(indexSize + offset, size);
+    }
+
+    @Override
+    public boolean read(long offset,
+                 ByteBuffer compressedBuffer, long compressedSize,
+                 ByteBuffer uncompressedBuffer, long uncompressedSize) {
+        assert compressedSize < Integer.MAX_VALUE;
+        assert uncompressedSize < Integer.MAX_VALUE;
+        boolean isRead = readBuffer(compressedBuffer,
+                                    indexSize + offset, compressedSize);
+        if (isRead) {
+            byte[] bytesIn = new byte[(int)compressedSize];
+            compressedBuffer.get(bytesIn);
+            byte[] bytesOut;
+            try {
+                bytesOut = decompressor.decompressResource(byteOrder, (int strOffset) -> {
+                    return new UTF8String(getStringBytes(strOffset)).toString();
+                }, bytesIn);
+            } catch (IOException ex) {
+                throw new RuntimeException(ex);
+            }
+            uncompressedBuffer.put(bytesOut);
+            uncompressedBuffer.rewind();
+        }
+
+        return isRead;
+    }
+
+    @Override
+    public boolean read(long offset,
+                 ByteBuffer uncompressedBuffer, long uncompressedSize) {
+        assert uncompressedSize < Integer.MAX_VALUE;
+        boolean isRead = readBuffer(uncompressedBuffer,
+                                    indexSize + offset, uncompressedSize);
+
+        return isRead;
+    }
+
+    @Override
+    public byte[] getStringBytes(int offset) {
+        if (offset == 0) {
+            return new byte[0];
+        }
+
+        int length = strings.length - offset;
+
+        for (int i = offset; i < strings.length; i++) {
+            if (strings[i] == 0) {
+                length = i - offset;
+                break;
+            }
+        }
+
+        byte[] bytes = new byte[length];
+        System.arraycopy(strings, offset, bytes, 0, length);
+
+        return bytes;
+    }
+
+    @Override
+    public long[] getAttributes(int offset) {
+        return ImageLocationBase.decompress(locations, offset);
+    }
+
+    @Override
+    public ImageLocation findLocation(UTF8String name, ImageStringsReader strings) {
+        int count = header.getTableLength();
+        int index = redirect[name.hashCode() % count];
+
+        if (index < 0) {
+            index = -index - 1;
+        } else {
+            index = name.hashCode(index) % count;
+        }
+
+        long[] attributes = getAttributes(offsets[index]);
+
+        ImageLocation imageLocation = new ImageLocation(attributes, strings);
+
+        if (!imageLocation.verify(name)) {
+            return null;
+        }
+
+        return imageLocation;
+   }
+
+    @Override
+    public int[] attributeOffsets() {
+        return offsets;
+    }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,369 +25,15 @@
 
 package jdk.internal.jimage;
 
-import java.nio.ByteBuffer;
-
-public final class ImageLocation {
-    final static int ATTRIBUTE_END = 0;
-    final static int ATTRIBUTE_BASE = 1;
-    final static int ATTRIBUTE_PARENT = 2;
-    final static int ATTRIBUTE_EXTENSION = 3;
-    final static int ATTRIBUTE_OFFSET = 4;
-    final static int ATTRIBUTE_COMPRESSED = 5;
-    final static int ATTRIBUTE_UNCOMPRESSED = 6;
-    final static int ATTRIBUTE_COUNT = 7;
-
-    private int locationOffset;
-    private long[] attributes;
-    private byte[] bytes;
-    private final ImageStrings strings;
-
-    private ImageLocation(ImageStrings strings) {
-        this.strings = strings;
-    }
-
-    void writeTo(ImageStream stream) {
-        compress();
-        locationOffset = stream.getPosition();
-        stream.put(bytes, 0, bytes.length);
-    }
-
-    static ImageLocation readFrom(ByteBuffer locationsBuffer, int offset, ImageStrings strings) {
-        final long[] attributes = new long[ATTRIBUTE_COUNT];
-
-        for (int i = offset; true; ) {
-            int data = locationsBuffer.get(i++) & 0xFF;
-            int kind = attributeKind(data);
-            assert ATTRIBUTE_END <= kind && kind < ATTRIBUTE_COUNT : "Invalid attribute kind";
-
-            if (kind == ATTRIBUTE_END) {
-                break;
-            }
-
-            int length = attributeLength(data);
-            long value = 0;
-
-            for (int j = 0; j < length; j++) {
-                value <<= 8;
-                value |= locationsBuffer.get(i++) & 0xFF;
-            }
-
-            attributes[kind] = value;
-        }
-
-        ImageLocation location =  new ImageLocation(strings);
-        location.attributes = attributes;
-
-        return location;
-    }
-
-    private static int attributeLength(int data) {
-        return (data & 0x7) + 1;
-    }
-
-    private static int attributeKind(int data) {
-        return data >>> 3;
-    }
-
-    public boolean verify(UTF8String name) {
-        UTF8String match = UTF8String.match(name, getParent());
-
-        if (match == null) {
-            return false;
-        }
-
-        match = UTF8String.match(match, getBase());
-
-        if (match == null) {
-            return false;
-        }
-
-        match = UTF8String.match(match, getExtension());
-
-        return match != null && match.length() == 0;
-    }
-
-
-    long getAttribute(int kind) {
-        assert ATTRIBUTE_END < kind && kind < ATTRIBUTE_COUNT : "Invalid attribute kind";
-        decompress();
-
-        return attributes[kind];
-    }
-
-    UTF8String getAttributeUTF8String(int kind) {
-        assert ATTRIBUTE_END < kind && kind < ATTRIBUTE_COUNT : "Invalid attribute kind";
-        decompress();
-
-        return strings.get((int)attributes[kind]);
-    }
-
-    String getAttributeString(int kind) {
-        return getAttributeUTF8String(kind).toString();
-    }
-
-    ImageLocation addAttribute(int kind, long value) {
-        assert ATTRIBUTE_END < kind && kind < ATTRIBUTE_COUNT : "Invalid attribute kind";
-        decompress();
-        attributes[kind] = value;
-        return this;
-    }
-
-    private void decompress() {
-        if (attributes == null) {
-            attributes = new long[ATTRIBUTE_COUNT];
-        }
-
-        if (bytes != null) {
-            for (int i = 0; i < bytes.length; ) {
-                int data = bytes[i++] & 0xFF;
-                int kind = attributeKind(data);
-
-                if (kind == ATTRIBUTE_END) {
-                    break;
-                }
-
-                assert ATTRIBUTE_END < kind && kind < ATTRIBUTE_COUNT : "Invalid attribute kind";
-                int length = attributeLength(data);
-                long value = 0;
-
-                for (int j = 0; j < length; j++) {
-                    value <<= 8;
-                    value |= bytes[i++] & 0xFF;
-                }
-
-                 attributes[kind] = value;
-            }
-
-            bytes = null;
-        }
-    }
-
-    private void compress() {
-        if (bytes == null) {
-            ImageStream stream = new ImageStream(16);
-
-            for (int kind = ATTRIBUTE_END + 1; kind < ATTRIBUTE_COUNT; kind++) {
-                long value = attributes[kind];
-
-                if (value != 0) {
-                    int n = (63 - Long.numberOfLeadingZeros(value)) >> 3;
-                    stream.put((kind << 3) | n);
-
-                    for (int i = n; i >= 0; i--) {
-                        stream.put((int)(value >> (i << 3)));
-                    }
-                }
-            }
-
-            stream.put(ATTRIBUTE_END << 3);
-            bytes = stream.toArray();
-            attributes = null;
-        }
+public final class ImageLocation  extends ImageLocationBase {
+    ImageLocation(long[] attributes, ImageStringsReader strings) {
+        super(attributes, strings);
     }
 
-    static ImageLocation newLocation(UTF8String fullname, ImageStrings strings, long contentOffset, long compressedSize, long uncompressedSize) {
-        UTF8String base;
-        UTF8String extension = extension(fullname);
-        int parentOffset = ImageStrings.EMPTY_OFFSET;
-        int extensionOffset = ImageStrings.EMPTY_OFFSET;
-        int baseOffset;
-
-        if (extension.length() != 0) {
-            UTF8String parent = parent(fullname);
-            base = base(fullname);
-            parentOffset = strings.add(parent);
-            extensionOffset = strings.add(extension);
-        } else {
-            base = fullname;
-        }
-
-        baseOffset = strings.add(base);
-
-        return new ImageLocation(strings)
-               .addAttribute(ATTRIBUTE_BASE, baseOffset)
-               .addAttribute(ATTRIBUTE_PARENT, parentOffset)
-               .addAttribute(ATTRIBUTE_EXTENSION, extensionOffset)
-               .addAttribute(ATTRIBUTE_OFFSET, contentOffset)
-               .addAttribute(ATTRIBUTE_COMPRESSED, compressedSize)
-               .addAttribute(ATTRIBUTE_UNCOMPRESSED, uncompressedSize);
-    }
-
-    @Override
-    public int hashCode() {
-        return getExtension().hashCode(getBase().hashCode(getParent().hashCode()));
-    }
-
-    int hashCode(int base) {
-        return getExtension().hashCode(getBase().hashCode(getParent().hashCode(base)));
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-
-        if (!(obj instanceof ImageLocation)) {
-            return false;
-        }
-
-        ImageLocation other = (ImageLocation)obj;
-
-        return getBaseOffset() == other.getBaseOffset() &&
-               getParentOffset() == other.getParentOffset() &&
-               getExtensionOffset() == other.getExtensionOffset();
-    }
-
-    static UTF8String parent(UTF8String fullname) {
-        int slash = fullname.lastIndexOf('/');
-
-        return slash == UTF8String.NOT_FOUND ? UTF8String.EMPTY_STRING : fullname.substring(0, slash + 1);
-    }
-
-    static UTF8String extension(UTF8String fullname) {
-        int dot = fullname.lastIndexOf('.');
-
-        return dot == UTF8String.NOT_FOUND ? UTF8String.EMPTY_STRING : fullname.substring(dot);
-    }
-
-    static UTF8String base(UTF8String fullname) {
-        int slash = fullname.lastIndexOf('/');
-
-        if (slash != UTF8String.NOT_FOUND) {
-            fullname = fullname.substring(slash + 1);
-        }
-
-        int dot = fullname.lastIndexOf('.');
-
-        if (dot != UTF8String.NOT_FOUND) {
-            fullname = fullname.substring(0, dot);
-        }
-
-        return fullname;
-    }
-
-    int getLocationOffset() {
-        return locationOffset;
-    }
-
-    UTF8String getBase() {
-        return getAttributeUTF8String(ATTRIBUTE_BASE);
-    }
-
-    public String getBaseString() {
-        return  getBase().toString();
-    }
-
-    int getBaseOffset() {
-        return (int)getAttribute(ATTRIBUTE_BASE);
-    }
-
-    UTF8String getParent() {
-        return getAttributeUTF8String(ATTRIBUTE_PARENT);
-    }
+    static ImageLocation readFrom(BasicImageReader reader, int offset) {
+        long[] attributes = reader.getAttributes(offset);
+        ImageStringsReader strings = reader.getStrings();
 
-    public String getParentString() {
-        return getParent().toString();
-    }
-
-    int getParentOffset() {
-        return (int)getAttribute(ATTRIBUTE_PARENT);
-    }
-
-    UTF8String getExtension() {
-        return getAttributeUTF8String(ATTRIBUTE_EXTENSION);
-    }
-
-    public String getExtensionString() {
-        return getExtension().toString();
-    }
-
-    int getExtensionOffset() {
-        return (int)getAttribute(ATTRIBUTE_EXTENSION);
-    }
-
-    UTF8String getName() {
-        return getBase().concat(getExtension());
-    }
-
-    String getNameString() {
-        return getName().toString();
-    }
-
-    UTF8String getFullname() {
-        return getParent().concat(getBase(), getExtension());
-    }
-
-    String getFullnameString() {
-        return getFullname().toString();
-    }
-
-    public long getContentOffset() {
-        return getAttribute(ATTRIBUTE_OFFSET);
-    }
-
-    public long getCompressedSize() {
-        return getAttribute(ATTRIBUTE_COMPRESSED);
-    }
-
-    public long getUncompressedSize() {
-        return getAttribute(ATTRIBUTE_UNCOMPRESSED);
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        decompress();
-
-        for (int kind = ATTRIBUTE_END + 1; kind < ATTRIBUTE_COUNT; kind++) {
-            long value = attributes[kind];
-
-            if (value == 0) {
-                continue;
-            }
-
-            switch (kind) {
-                case ATTRIBUTE_BASE:
-                    sb.append("Base: ");
-                    sb.append(value);
-                    sb.append(' ');
-                    sb.append(strings.get((int)value).toString());
-                    break;
-
-                case ATTRIBUTE_PARENT:
-                    sb.append("Parent: ");
-                    sb.append(value);
-                    sb.append(' ');
-                    sb.append(strings.get((int)value).toString());
-                    break;
-
-                case ATTRIBUTE_EXTENSION:
-                    sb.append("Extension: ");
-                    sb.append(value);
-                    sb.append(' ');
-                    sb.append(strings.get((int)value).toString());
-                    break;
-
-                case ATTRIBUTE_OFFSET:
-                    sb.append("Offset: ");
-                    sb.append(value);
-                    break;
-
-                case ATTRIBUTE_COMPRESSED:
-                    sb.append("Compressed: ");
-                    sb.append(value);
-                    break;
-
-                case ATTRIBUTE_UNCOMPRESSED:
-                    sb.append("Uncompressed: ");
-                    sb.append(value);
-                    break;
-           }
-
-           sb.append("; ");
-        }
-
-        return sb.toString();
+        return new ImageLocation(attributes, strings);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageLocationBase.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,264 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jimage;
+
+public class ImageLocationBase {
+    final static int ATTRIBUTE_END = 0;
+    final static int ATTRIBUTE_MODULE = 1;
+    final static int ATTRIBUTE_PARENT = 2;
+    final static int ATTRIBUTE_BASE = 3;
+    final static int ATTRIBUTE_EXTENSION = 4;
+    final static int ATTRIBUTE_OFFSET = 5;
+    final static int ATTRIBUTE_COMPRESSED = 6;
+    final static int ATTRIBUTE_UNCOMPRESSED = 7;
+    final static int ATTRIBUTE_COUNT = 8;
+
+    protected final long[] attributes;
+
+    protected final ImageStrings strings;
+
+    protected ImageLocationBase(long[] attributes, ImageStrings strings) {
+        this.attributes = attributes;
+        this.strings = strings;
+    }
+
+    ImageStrings getStrings() {
+        return strings;
+    }
+
+    private static int attributeLength(int data) {
+        return (data & 0x7) + 1;
+    }
+
+    private static int attributeKind(int data) {
+        return data >>> 3;
+    }
+
+    static long[] decompress(byte[] bytes) {
+        return decompress(bytes, 0);
+    }
+
+    static long[] decompress(byte[] bytes, int offset) {
+        long[] attributes = new long[ATTRIBUTE_COUNT];
+
+        if (bytes != null) {
+            for (int i = offset; i < bytes.length; ) {
+                int data = bytes[i++] & 0xFF;
+                int kind = attributeKind(data);
+
+                if (kind == ATTRIBUTE_END) {
+                    break;
+                }
+
+                assert ATTRIBUTE_END < kind &&
+                       kind < ATTRIBUTE_COUNT : "Invalid attribute kind";
+                int length = attributeLength(data);
+                long value = 0;
+
+                for (int j = 0; j < length; j++) {
+                    value <<= 8;
+                    value |= bytes[i++] & 0xFF;
+                }
+
+                 attributes[kind] = value;
+            }
+        }
+
+        return attributes;
+    }
+
+    static byte[] compress(long[] attributes) {
+        ImageStream stream = new ImageStream(16);
+
+        for (int kind = ATTRIBUTE_END + 1; kind < ATTRIBUTE_COUNT; kind++) {
+            long value = attributes[kind];
+
+            if (value != 0) {
+                int n = (63 - Long.numberOfLeadingZeros(value)) >> 3;
+                stream.put((kind << 3) | n);
+
+                for (int i = n; i >= 0; i--) {
+                    stream.put((int)(value >> (i << 3)));
+                }
+            }
+        }
+
+        stream.put(ATTRIBUTE_END << 3);
+
+        return stream.toArray();
+     }
+
+    public boolean verify(UTF8String name) {
+        return UTF8String.equals(getFullName(), name);
+    }
+
+    protected long getAttribute(int kind) {
+        assert ATTRIBUTE_END < kind &&
+               kind < ATTRIBUTE_COUNT : "Invalid attribute kind";
+
+        return attributes[kind];
+    }
+
+    protected UTF8String getAttributeUTF8String(int kind) {
+        assert ATTRIBUTE_END < kind &&
+               kind < ATTRIBUTE_COUNT : "Invalid attribute kind";
+
+        return getStrings().get((int)attributes[kind]);
+    }
+
+    protected String getAttributeString(int kind) {
+        return getAttributeUTF8String(kind).toString();
+    }
+
+    UTF8String getModule() {
+        return getAttributeUTF8String(ATTRIBUTE_MODULE);
+    }
+
+    public String getModuleString() {
+        return getModule().toString();
+    }
+
+    int getModuleOffset() {
+        return (int)getAttribute(ATTRIBUTE_MODULE);
+    }
+
+    UTF8String getBase() {
+        return getAttributeUTF8String(ATTRIBUTE_BASE);
+    }
+
+    public String getBaseString() {
+        return  getBase().toString();
+    }
+
+    int getBaseOffset() {
+        return (int)getAttribute(ATTRIBUTE_BASE);
+    }
+
+    UTF8String getParent() {
+        return getAttributeUTF8String(ATTRIBUTE_PARENT);
+    }
+
+    public String getParentString() {
+        return getParent().toString();
+    }
+
+    int getParentOffset() {
+        return (int)getAttribute(ATTRIBUTE_PARENT);
+    }
+
+    UTF8String getExtension() {
+        return getAttributeUTF8String(ATTRIBUTE_EXTENSION);
+    }
+
+    public String getExtensionString() {
+        return getExtension().toString();
+    }
+
+    int getExtensionOffset() {
+        return (int)getAttribute(ATTRIBUTE_EXTENSION);
+    }
+
+    UTF8String getFullName() {
+        return getFullName(false);
+    }
+
+    UTF8String getFullName(boolean modulesPrefix) {
+        // Note: Consider a UTF8StringBuilder.
+        UTF8String fullName = UTF8String.EMPTY_STRING;
+
+        if (getModuleOffset() != 0) {
+            fullName = fullName.concat(
+                // TODO The use of UTF8String.MODULES_STRING does not belong here.
+                modulesPrefix? UTF8String.MODULES_STRING :
+                               UTF8String.EMPTY_STRING,
+                UTF8String.SLASH_STRING,
+                getModule(),
+                UTF8String.SLASH_STRING);
+        }
+
+        if (getParentOffset() != 0) {
+            fullName = fullName.concat(getParent(),
+                                       UTF8String.SLASH_STRING);
+        }
+
+        fullName = fullName.concat(getBase());
+
+        if (getExtensionOffset() != 0) {
+                fullName = fullName.concat(UTF8String.DOT_STRING,
+                                           getExtension());
+        }
+
+        return fullName;
+    }
+
+    UTF8String buildName(boolean includeModule, boolean includeParent,
+            boolean includeName) {
+        // Note: Consider a UTF8StringBuilder.
+        UTF8String name = UTF8String.EMPTY_STRING;
+
+        if (includeModule && getModuleOffset() != 0) {
+            name = name.concat(UTF8String.MODULES_STRING,
+                               UTF8String.SLASH_STRING,
+                               getModule());
+        }
+
+        if (includeParent && getParentOffset() != 0) {
+            name = name.concat(UTF8String.SLASH_STRING,
+                                       getParent());
+        }
+
+        if (includeName) {
+            if (includeModule || includeParent) {
+                name = name.concat(UTF8String.SLASH_STRING);
+            }
+
+            name = name.concat(getBase());
+
+            if (getExtensionOffset() != 0) {
+                name = name.concat(UTF8String.DOT_STRING,
+                                           getExtension());
+            }
+        }
+
+        return name;
+    }
+
+    String getFullNameString() {
+        return getFullName().toString();
+    }
+
+    public long getContentOffset() {
+        return getAttribute(ATTRIBUTE_OFFSET);
+    }
+
+    public long getCompressedSize() {
+        return getAttribute(ATTRIBUTE_COMPRESSED);
+    }
+
+    public long getUncompressedSize() {
+        return getAttribute(ATTRIBUTE_UNCOMPRESSED);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageLocationWriter.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,140 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jimage;
+
+public final class ImageLocationWriter extends ImageLocationBase {
+    private int locationOffset;
+
+    private ImageLocationWriter(ImageStringsWriter strings) {
+        super(new long[ATTRIBUTE_COUNT], strings);
+    }
+
+    void writeTo(ImageStream stream) {
+        byte[] bytes = ImageLocation.compress(attributes);
+        locationOffset = stream.getPosition();
+        stream.put(bytes, 0, bytes.length);
+    }
+
+    private ImageLocationWriter addAttribute(int kind, long value) {
+        assert ATTRIBUTE_END < kind &&
+               kind < ATTRIBUTE_COUNT : "Invalid attribute kind";
+        attributes[kind] = value;
+        return this;
+    }
+
+    private ImageLocationWriter addAttribute(int kind, UTF8String value) {
+        return addAttribute(kind, strings.add(value));
+    }
+
+    static ImageLocationWriter newLocation(UTF8String fullName,
+            ImageStringsWriter strings,
+            long contentOffset, long compressedSize, long uncompressedSize) {
+        UTF8String moduleName = UTF8String.EMPTY_STRING;
+        UTF8String parentName = UTF8String.EMPTY_STRING;
+        UTF8String baseName;
+        UTF8String extensionName = UTF8String.EMPTY_STRING;
+
+        int offset = fullName.indexOf('/', 1);
+        if (fullName.length() >= 2 && fullName.charAt(0) == '/' && offset != -1) {
+            moduleName = fullName.substring(1, offset - 1);
+            fullName = fullName.substring(offset + 1);
+        }
+
+        offset = fullName.lastIndexOf('/');
+        if (offset != -1) {
+            parentName = fullName.substring(0, offset);
+            fullName = fullName.substring(offset + 1);
+        }
+
+        offset = fullName.lastIndexOf('.');
+        if (offset != -1) {
+            baseName = fullName.substring(0, offset);
+            extensionName = fullName.substring(offset + 1);
+        } else {
+            baseName = fullName;
+        }
+
+        return new ImageLocationWriter(strings)
+               .addAttribute(ATTRIBUTE_MODULE, moduleName)
+               .addAttribute(ATTRIBUTE_PARENT, parentName)
+               .addAttribute(ATTRIBUTE_BASE, baseName)
+               .addAttribute(ATTRIBUTE_EXTENSION, extensionName)
+               .addAttribute(ATTRIBUTE_OFFSET, contentOffset)
+               .addAttribute(ATTRIBUTE_COMPRESSED, compressedSize)
+               .addAttribute(ATTRIBUTE_UNCOMPRESSED, uncompressedSize);
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode(UTF8String.HASH_MULTIPLIER);
+    }
+
+    int hashCode(int seed) {
+        int hash = seed;
+
+        if (getModuleOffset() != 0) {
+            hash = UTF8String.SLASH_STRING.hashCode(hash);
+            hash = getModule().hashCode(hash);
+            hash = UTF8String.SLASH_STRING.hashCode(hash);
+        }
+
+        if (getParentOffset() != 0) {
+            hash = getParent().hashCode(hash);
+            hash = UTF8String.SLASH_STRING.hashCode(hash);
+        }
+
+        hash = getBase().hashCode(hash);
+
+        if (getExtensionOffset() != 0) {
+            hash = UTF8String.DOT_STRING.hashCode(hash);
+            hash = getExtension().hashCode(hash);
+        }
+
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (!(obj instanceof ImageLocationWriter)) {
+            return false;
+        }
+
+        ImageLocation other = (ImageLocation)obj;
+
+        return getModuleOffset() == other.getModuleOffset() &&
+               getParentOffset() == other.getParentOffset() &&
+               getBaseOffset() == other.getBaseOffset() &&
+               getExtensionOffset() == other.getExtensionOffset();
+    }
+
+    int getLocationOffset() {
+        return locationOffset;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageModuleData.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,288 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jimage;
+
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/*
+ * Manage module meta data.
+ *
+ * NOTE: needs revision.
+ * Each loader requires set of module meta data to identify which modules and
+ * packages are managed by that loader.  Currently, there is one image file per
+ * loader, so only one  module meta data resource per file.
+ *
+ * Each element in the module meta data is a native endian 4 byte integer.  Note
+ * that entries with zero offsets for string table entries should be ignored (
+ * padding for hash table lookup.)
+ *
+ * Format:
+ *    Count of package to module entries
+ *    Count of module to package entries
+ *    Perfect Hash redirect table[Count of package to module entries]
+ *    Package to module entries[Count of package to module entries]
+ *        Offset to package name in string table
+ *        Offset to module name in string table
+ *    Perfect Hash redirect table[Count of module to package entries]
+ *    Module to package entries[Count of module to package entries]
+ *        Offset to module name in string table
+ *        Count of packages in module
+ *        Offset to first package in packages table
+ *    Packages[]
+ *        Offset to package name in string table
+ */
+
+final public class ImageModuleData {
+    public final static String META_DATA_EXTENSION = ".jdata";
+    public final static String SEPARATOR = "\t";
+    public final static int NOT_FOUND = -1;
+    private final static int ptmCountOffset = 0;
+    private final static int mtpCountOffset = 1;
+    private final static int ptmRedirectOffset = 2;
+    private final static int dataNameOffset = 0;
+    private final static int ptmDataWidth = 2;
+    private final static int ptmDataModuleOffset = 1;
+    private final static int mtpDataWidth = 3;
+    private final static int mtpDataCountOffset = 1;
+    private final static int mtpDataOffsetOffset = 2;
+
+    private final BasicImageReader reader;
+    private final IntBuffer intBuffer;
+    private final int ptmRedirectLength;
+    private final int mtpRedirectLength;
+    private final int ptmDataOffset;
+    private final int mtpRedirectOffset;
+    private final int mtpDataOffset;
+    private final int mtpPackagesOffset;
+
+    public ImageModuleData(BasicImageReader reader) {
+         this(reader, getBytes(reader));
+    }
+
+    public ImageModuleData(BasicImageReader reader, byte[] bytes) {
+        this.reader = reader;
+
+        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(reader.getByteOrder());
+        this.intBuffer = byteBuffer.asIntBuffer();
+
+        this.ptmRedirectLength = get(ptmCountOffset);
+        this.mtpRedirectLength = get(mtpCountOffset);
+
+        this.ptmDataOffset = ptmRedirectOffset + ptmRedirectLength;
+        this.mtpRedirectOffset = ptmDataOffset + ptmRedirectLength * ptmDataWidth;
+        this.mtpDataOffset = mtpRedirectOffset + mtpRedirectLength;
+        this.mtpPackagesOffset = mtpDataOffset + mtpRedirectLength * mtpDataWidth;
+    }
+
+    private static byte[] getBytes(BasicImageReader reader) {
+        String loaderName = reader.imagePathName();
+
+        if (loaderName.endsWith(BasicImageWriter.IMAGE_EXT)) {
+            loaderName = loaderName.substring(0, loaderName.length() -
+                    BasicImageWriter.IMAGE_EXT.length());
+        }
+
+        byte[] bytes = reader.getResource(getModuleDataName(loaderName));
+
+        if (bytes == null) {
+            throw new InternalError("module data missing");
+        }
+
+        return bytes;
+    }
+
+    public List<String> fromModulePackages() {
+        List<String> lines = new ArrayList<>();
+
+        for (int i = 0; i < mtpRedirectLength; i++) {
+            int index = mtpDataOffset + i * mtpDataWidth;
+            int offset = get(index + dataNameOffset);
+
+            if (offset != 0) {
+                StringBuilder sb = new StringBuilder();
+
+                sb.append(getString(offset));
+
+                int count = get(index + mtpDataCountOffset);
+                int base = get(index + mtpDataOffsetOffset) + mtpPackagesOffset;
+
+                for (int j = 0; j < count; j++) {
+                    sb.append(SEPARATOR);
+                    sb.append(stringAt(base + j));
+                }
+
+                lines.add(sb.toString());
+            }
+        }
+
+        return lines;
+    }
+
+    public static String getModuleDataName(String loaderName) {
+        return loaderName + META_DATA_EXTENSION;
+    }
+
+    private int get(int index) {
+        return intBuffer.get(index);
+    }
+
+    private String getString(int offset) {
+        return reader.getString(offset);
+    }
+
+    private String stringAt(int index) {
+        return reader.getString(get(index));
+    }
+
+    private UTF8String getUTF8String(int offset) {
+        return reader.getUTF8String(offset);
+    }
+
+    private UTF8String utf8StringAt(int index) {
+        return reader.getUTF8String(get(index));
+    }
+
+    private int find(UTF8String name, int baseOffset, int length, int width) {
+        if (length == 0) {
+            return NOT_FOUND;
+        }
+
+        int hashCode = name.hashCode();
+        int index = hashCode % length;
+        int value = get(baseOffset + index);
+
+        if (value > 0 ) {
+            hashCode = name.hashCode(value);
+            index = hashCode % length;
+        } else if (value < 0) {
+            index = -1 - value;
+        } else {
+            return NOT_FOUND;
+        }
+
+        index = baseOffset + length + index * width;
+
+        if (!utf8StringAt(index + dataNameOffset).equals(name)) {
+            return NOT_FOUND;
+        }
+
+        return index;
+    }
+
+    public String packageToModule(String packageName) {
+        UTF8String moduleName = packageToModule(new UTF8String(packageName));
+
+        return moduleName != null ? moduleName.toString() : null;
+    }
+
+    public UTF8String packageToModule(UTF8String packageName) {
+        int index = find(packageName, ptmRedirectOffset, ptmRedirectLength, ptmDataWidth);
+
+        if (index != NOT_FOUND) {
+            return utf8StringAt(index + ptmDataModuleOffset);
+        }
+
+        return null;
+    }
+
+    public List<String> moduleToPackages(String moduleName) {
+        int index = find(new UTF8String(moduleName), mtpRedirectOffset,
+                mtpRedirectLength, mtpDataWidth);
+
+        if (index != NOT_FOUND) {
+            int count = get(index + mtpDataCountOffset);
+            int base = get(index + mtpDataOffsetOffset) + mtpPackagesOffset;
+            List<String> packages = new ArrayList<>(count);
+
+            for (int i = 0; i < count; i++) {
+                packages.add(stringAt(base + i));
+            }
+
+            return packages;
+        }
+
+        return null;
+    }
+
+    public List<String> allPackageNames() {
+        List<String> packages = new ArrayList<>();
+
+        for (int i = 0; i < ptmRedirectLength; i++) {
+            int offset = get(ptmDataOffset + i * ptmDataWidth + dataNameOffset);
+
+            if (offset != 0) {
+                packages.add(getString(offset));
+            }
+        }
+
+        return packages;
+    }
+
+    public Set<String> allModuleNames() {
+        Set<String> modules = new HashSet<>();
+
+        for (int i = 0; i < mtpRedirectLength; i++) {
+            int index = mtpDataOffset + i * mtpDataWidth;
+            int offset = get(index + dataNameOffset);
+
+            if (offset != 0) {
+                modules.add(getString(offset));
+            }
+        }
+
+        return modules;
+    }
+
+    public Map<String, String> packageModuleMap() {
+        Map<String, String> map = new HashMap<>();
+
+        for (int i = 0; i < mtpRedirectLength; i++) {
+            int index = mtpDataOffset + i * mtpDataWidth;
+            int offset = get(index + dataNameOffset);
+
+            if (offset != 0) {
+                String moduleName = getString(offset);
+
+                int count = get(index + mtpDataCountOffset);
+                int base = get(index + mtpDataOffsetOffset) + mtpPackagesOffset;
+
+                for (int j = 0; j < count; j++) {
+                    map.put(stringAt(base + j), moduleName);
+                }
+            }
+        }
+
+        return map;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageModuleDataWriter.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,165 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jimage;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class ImageModuleDataWriter {
+    final byte[] bytes;
+
+    public ImageModuleDataWriter(BasicImageWriter writer,
+            Map<String, List<String>> modulePackages) {
+        PerfectHashBuilder<String> packageToModule = new PerfectHashBuilder<>(
+                new PerfectHashBuilder.Entry<String>().getClass(),
+                new PerfectHashBuilder.Bucket<String>().getClass());
+        PerfectHashBuilder<List<String>> moduleToPackages = new PerfectHashBuilder<>(
+                new PerfectHashBuilder.Entry<List<String>>().getClass(),
+                new PerfectHashBuilder.Bucket<List<String>>().getClass());
+
+        modulePackages.entrySet().stream().forEach((entry) -> {
+            String moduleName = entry.getKey();
+            List<String> packages = entry.getValue();
+            packages.stream().forEach((packageName) -> {
+                packageToModule.put(packageName, moduleName);
+            });
+
+            moduleToPackages.put(moduleName, packages);
+        });
+
+        packageToModule.generate();
+        moduleToPackages.generate();
+
+        bytes = getBytes(writer, packageToModule, moduleToPackages);
+    }
+
+    public static ImageModuleDataWriter buildModuleData(BasicImageWriter writer,
+            Map<String, Set<String>> modulePackagesMap) {
+        Set<String> modules = modulePackagesMap.keySet();
+
+        Map<String, List<String>> modulePackages = new LinkedHashMap<>();
+        modules.stream().sorted().forEach((moduleName) -> {
+            List<String> localPackages = modulePackagesMap.get(moduleName).stream()
+                    .map(pn -> pn.replace('.', '/'))
+                    .sorted()
+                    .collect(Collectors.toList());
+            modulePackages.put(moduleName, localPackages);
+        });
+
+        return new ImageModuleDataWriter(writer, modulePackages);
+    }
+
+    public static Map<String, List<String>> toModulePackages(List<String> lines) {
+        Map<String, List<String>> modulePackages = new LinkedHashMap<>();
+
+        for (String line : lines) {
+            String[] parts = line.split(ImageModuleData.SEPARATOR);
+            String moduleName = parts[0];
+            List<String> packages = Arrays.asList(Arrays.copyOfRange(parts, 1, parts.length));
+            modulePackages.put(moduleName, packages);
+        }
+
+        return modulePackages;
+    }
+
+    public void addLocation(String name, BasicImageWriter writer) {
+        writer.addLocation(ImageModuleData.getModuleDataName(name), 0, 0, bytes.length);
+    }
+
+    private byte[] getBytes(BasicImageWriter writer,
+            PerfectHashBuilder<String> packageToModule,
+            PerfectHashBuilder<List<String>> moduleToPackages) {
+        ImageStream stream = new ImageStream(writer.getByteOrder());
+
+        int[] ptmRedirect = packageToModule.getRedirect();
+        int[] mtpRedirect = moduleToPackages.getRedirect();
+        PerfectHashBuilder.Entry<String>[] ptmOrder = packageToModule.getOrder();
+        PerfectHashBuilder.Entry<List<String>>[] mtpOrder = moduleToPackages.getOrder();
+
+        stream.putInt(ptmRedirect.length);
+        stream.putInt(mtpRedirect.length);
+
+        for (int value : ptmRedirect) {
+            stream.putInt(value);
+        }
+
+        for (PerfectHashBuilder.Entry<String> entry : ptmOrder) {
+            if (entry != null) {
+                stream.putInt(writer.addString(entry.getKey()));
+                stream.putInt(writer.addString(entry.getValue()));
+            } else {
+                stream.putInt(0);
+                stream.putInt(0);
+            }
+        }
+
+        for (int value : mtpRedirect) {
+            stream.putInt(value);
+        }
+
+        int index = 0;
+
+        for (PerfectHashBuilder.Entry<List<String>> entry : mtpOrder) {
+            if (entry != null) {
+                int count = entry.getValue().size();
+                stream.putInt(writer.addString(entry.getKey()));
+                stream.putInt(count);
+                stream.putInt(index);
+                index += count;
+            } else {
+                stream.putInt(0);
+                stream.putInt(0);
+                stream.putInt(0);
+            }
+        }
+
+        for (PerfectHashBuilder.Entry<List<String>> entry : mtpOrder) {
+            if (entry != null) {
+                List<String> value = entry.getValue();
+                value.stream().forEach((packageName) -> {
+                    stream.putInt(writer.addString(packageName));
+                });
+            }
+        }
+
+        return stream.toArray();
+    }
+
+    public void writeTo(DataOutputStream out) throws IOException {
+         out.write(bytes, 0, bytes.length);
+    }
+
+    public int size() {
+        return bytes.length;
+    }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageModules.java	Wed Jul 05 20:41:30 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.internal.jimage;
-
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static jdk.internal.jimage.PackageModuleMap.*;
-
-public class ImageModules {
-    protected final Map<Loader, LoaderModuleData> loaders = new LinkedHashMap<>();
-    protected final Map<String, Set<String>> localPkgs = new HashMap<>();
-
-    protected ImageModules() {}
-
-    public ImageModules(Set<String> bootModules,
-                        Set<String> extModules,
-                        Set<String> appModules) throws IOException {
-        mapModulesToLoader(Loader.BOOT_LOADER, bootModules);
-        mapModulesToLoader(Loader.EXT_LOADER, extModules);
-        mapModulesToLoader(Loader.APP_LOADER, appModules);
-    }
-
-    public Map<String, Set<String>> packages() {
-        return localPkgs;
-    }
-
-    // ## FIXME: should be package-private
-    // When jlink legacy format support is removed, it should
-    // use the package table in the jimage.
-    public void setPackages(String mn, Set<String> pkgs) {
-        localPkgs.put(mn, pkgs);
-    }
-
-    /*
-     * Returns the name of modules mapped to a given class loader in the image
-     */
-    public Set<String> getModules(Loader type) {
-        if (loaders.containsKey(type)) {
-            return loaders.get(type).modules();
-        } else {
-            return Collections.emptySet();
-        }
-    }
-
-    private void mapModulesToLoader(Loader loader, Set<String> modules) {
-        if (modules.isEmpty())
-            return;
-
-        // put java.base first
-        Set<String> mods = new LinkedHashSet<>();
-        modules.stream()
-               .filter(m -> m.equals("java.base"))
-               .forEach(mods::add);
-        modules.stream().sorted()
-               .filter(m -> !m.equals("java.base"))
-               .forEach(mods::add);
-        loaders.put(loader, new LoaderModuleData(loader, mods));
-    }
-
-    enum Loader {
-        BOOT_LOADER(0, "bootmodules"),
-        EXT_LOADER(1, "extmodules"),
-        APP_LOADER(2, "appmodules");  // ## may be more than 1 loader
-
-        final int id;
-        final String name;
-        Loader(int id, String name) {
-            this.id = id;
-            this.name = name;
-        }
-
-        String getName() {
-            return name;
-        }
-        static Loader get(int id) {
-            switch (id) {
-                case 0: return BOOT_LOADER;
-                case 1: return EXT_LOADER;
-                case 2: return APP_LOADER;
-                default:
-                    throw new IllegalArgumentException("invalid loader id: " + id);
-            }
-        }
-        public int id() { return id; }
-    }
-
-    public class LoaderModuleData {
-        private final Loader loader;
-        private final Set<String> modules;
-        LoaderModuleData(Loader loader, Set<String> modules) {
-            this.loader = loader;
-            this.modules = Collections.unmodifiableSet(modules);
-        }
-
-        Set<String> modules() {
-            return modules;
-        }
-        Loader loader() { return loader; }
-    }
-
-    ModuleIndex buildModuleIndex(Loader type, BasicImageWriter writer) {
-        return new ModuleIndex(getModules(type), writer);
-    }
-
-    /*
-     * Generate module name table and the package map as resources
-     * in the modular image
-     */
-    public class ModuleIndex {
-        final Map<String, Integer> moduleOffsets = new LinkedHashMap<>();
-        final Map<String, List<Integer>> packageOffsets = new HashMap<>();
-        final int size;
-        public ModuleIndex(Set<String> mods, BasicImageWriter writer) {
-            // module name offsets
-            writer.addLocation(MODULES_ENTRY, 0, 0, mods.size() * 4);
-            long offset = mods.size() * 4;
-            for (String mn : mods) {
-                moduleOffsets.put(mn, writer.addString(mn));
-                List<Integer> poffsets = localPkgs.get(mn).stream()
-                        .map(pn -> pn.replace('.', '/'))
-                        .map(writer::addString)
-                        .collect(Collectors.toList());
-                // package name offsets per module
-                String entry = mn + "/" + PACKAGES_ENTRY;
-                int bytes = poffsets.size() * 4;
-                writer.addLocation(entry, offset, 0, bytes);
-                offset += bytes;
-                packageOffsets.put(mn, poffsets);
-            }
-            this.size = (int) offset;
-        }
-
-        void writeTo(DataOutputStream out) throws IOException {
-            for (int moffset : moduleOffsets.values()) {
-                out.writeInt(moffset);
-            }
-            for (String mn : moduleOffsets.keySet()) {
-                for (int poffset : packageOffsets.get(mn)) {
-                    out.writeInt(poffset);
-                }
-            }
-        }
-
-        int size() {
-            return size;
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageNativeSubstrate.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,134 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import sun.misc.JavaNioAccess;
+import sun.misc.SharedSecrets;
+
+final class ImageNativeSubstrate implements ImageSubstrate {
+    private static final JavaNioAccess NIOACCESS =
+            SharedSecrets.getJavaNioAccess();
+
+    private final long id;
+    private final long indexAddress;
+    private final long dataAddress;
+
+    native static long openImage(String imagePath, boolean bigEndian);
+    native static void closeImage(long id);
+    native static long getIndexAddress(long id);
+    native static long getDataAddress(long id);
+    native static boolean readCompressed(long id, long offset,
+            ByteBuffer compressedBuffer, long compressedSize,
+            ByteBuffer uncompressedBuffer, long uncompressedSize);
+    native static boolean read(long id, long offset,
+            ByteBuffer uncompressedBuffer, long uncompressedSize);
+    native static byte[] getStringBytes(long id, int offset);
+    native static long[] getAttributes(long id, int offset);
+    native static long[] findAttributes(long id, byte[] path);
+    native static int[] attributeOffsets(long id);
+
+    static ByteBuffer newDirectByteBuffer(long address, long capacity) {
+        assert capacity < Integer.MAX_VALUE;
+        return NIOACCESS.newDirectByteBuffer(address, (int)capacity, null);
+    }
+
+    private ImageNativeSubstrate(long id) {
+        this.id = id;
+        this.indexAddress = getIndexAddress(id);
+        this.dataAddress = getDataAddress(id);
+    }
+
+    static ImageSubstrate openImage(String imagePath, ByteOrder byteOrder)
+            throws IOException {
+        long id = openImage(imagePath, byteOrder == ByteOrder.BIG_ENDIAN);
+
+        if (id == 0) {
+             throw new IOException("Image not found \"" + imagePath + "\"");
+        }
+
+        return new ImageNativeSubstrate(id);
+    }
+
+    @Override
+    public void close() {
+        closeImage(id);
+    }
+
+    @Override
+    public ByteBuffer getIndexBuffer(long offset, long size) {
+        return newDirectByteBuffer(indexAddress + offset, size);
+    }
+
+    @Override
+    public ByteBuffer getDataBuffer(long offset, long size) {
+        return dataAddress != 0 ?
+                newDirectByteBuffer(dataAddress + offset, size) : null;
+    }
+
+    @Override
+    public boolean supportsDataBuffer() {
+        return dataAddress != 0;
+    }
+
+    @Override
+    public boolean read(long offset,
+                 ByteBuffer compressedBuffer, long compressedSize,
+                 ByteBuffer uncompressedBuffer, long uncompressedSize) {
+        return readCompressed(id, offset,
+                    compressedBuffer, compressedSize,
+                    uncompressedBuffer, uncompressedSize);
+    }
+
+    @Override
+    public boolean read(long offset,
+                 ByteBuffer uncompressedBuffer, long uncompressedSize) {
+        return read(id, offset, uncompressedBuffer, uncompressedSize);
+    }
+
+    @Override
+    public byte[] getStringBytes(int offset) {
+        return getStringBytes(id, offset);
+    }
+
+    @Override
+    public long[] getAttributes(int offset) {
+        return getAttributes(id, offset);
+    }
+
+    @Override
+    public ImageLocation findLocation(UTF8String name, ImageStringsReader strings) {
+        long[] attributes = findAttributes(id, name.getBytes());
+
+        return attributes != null ? new ImageLocation(attributes, strings) : null;
+    }
+
+    @Override
+    public int[] attributeOffsets() {
+        return attributeOffsets(id);
+    }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java	Thu Jul 09 22:46:18 2015 -0700
@@ -26,12 +26,10 @@
 
 import java.io.IOException;
 import java.io.UncheckedIOException;
-import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.IntBuffer;
 import java.nio.file.Files;
-import java.nio.file.FileSystem;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.FileTime;
 import java.nio.file.Paths;
@@ -42,13 +40,11 @@
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
-import java.util.function.Supplier;
+import static jdk.internal.jimage.UTF8String.*;
 
 public class ImageReader extends BasicImageReader {
     // well-known strings needed for image file system.
-    static final UTF8String ROOT = new UTF8String("/");
-    static final UTF8String META_INF = new UTF8String("/META-INF");
-    static final UTF8String PACKAGES_OFFSETS = new UTF8String("packages.offsets");
+    static final UTF8String ROOT_STRING = UTF8String.SLASH_STRING;
 
     // attributes of the .jimage file. jimage file does not contain
     // attributes for the individual resources (yet). We use attributes
@@ -56,15 +52,18 @@
     // Iniitalized lazily, see {@link #imageFileAttributes()}.
     private BasicFileAttributes imageFileAttributes;
 
-    private final Map<String, String> packageMap;
+    private final ImageModuleData moduleData;
 
     // directory management implementation
     private final Map<UTF8String, Node> nodes;
     private volatile Directory rootDir;
 
+    private Directory packagesDir;
+    private Directory modulesDir;
+
     ImageReader(String imagePath, ByteOrder byteOrder) throws IOException {
         super(imagePath, byteOrder);
-        this.packageMap = PackageModuleMap.readFrom(this);
+        this.moduleData = new ImageModuleData(this);
         this.nodes = Collections.synchronizedMap(new HashMap<>());
     }
 
@@ -89,11 +88,42 @@
         clearNodes();
     }
 
+    @Override
+    public ImageLocation findLocation(UTF8String name) {
+        ImageLocation location = super.findLocation(name);
+
+        // NOTE: This should be removed when module system is up in full.
+        if (location == null) {
+            int index = name.lastIndexOf('/');
+
+            if (index != -1) {
+                UTF8String packageName = name.substring(0, index);
+                UTF8String moduleName = moduleData.packageToModule(packageName);
+
+                if (moduleName != null) {
+                    UTF8String fullName = UTF8String.SLASH_STRING.concat(moduleName,
+                            UTF8String.SLASH_STRING, name);
+                    location = super.findLocation(fullName);
+                }
+            } else {
+                // No package, try all modules.
+                for (String mod : moduleData.allModuleNames()) {
+                    location = super.findLocation("/" + mod + "/" + name);
+                    if (location != null) {
+                        break;
+                    }
+                }
+            }
+        }
+
+        return location;
+    }
+
     /**
      * Return the module name that contains the given package name.
      */
-    public String getModule(String pkg) {
-        return packageMap.get(pkg);
+    public String getModule(String packageName) {
+        return moduleData.packageToModule(packageName);
     }
 
     // jimage file does not store directory structure. We build nodes
@@ -101,14 +131,13 @@
     // Node can be a directory or a resource
     public static abstract class Node {
         private static final int ROOT_DIR = 0b0000_0000_0000_0001;
-        private static final int MODULE_DIR = 0b0000_0000_0000_0010;
-        private static final int METAINF_DIR = 0b0000_0000_0000_0100;
-        private static final int TOPLEVEL_PKG_DIR = 0b0000_0000_0000_1000;
-        private static final int HIDDEN = 0b0000_0000_0001_0000;
+        private static final int PACKAGES_DIR = 0b0000_0000_0000_0010;
+        private static final int MODULES_DIR = 0b0000_0000_0000_0100;
 
         private int flags;
         private final UTF8String name;
         private final BasicFileAttributes fileAttrs;
+        private boolean completed;
 
         Node(UTF8String name, BasicFileAttributes fileAttrs) {
             assert name != null;
@@ -117,6 +146,19 @@
             this.fileAttrs = fileAttrs;
         }
 
+        /**
+         * A node is completed when all its direct children have been built.
+         *
+         * @return
+         */
+        public boolean isCompleted() {
+            return completed;
+        }
+
+        public void setCompleted(boolean completed) {
+            this.completed = completed;
+        }
+
         public final void setIsRootDir() {
             flags |= ROOT_DIR;
         }
@@ -125,40 +167,20 @@
             return (flags & ROOT_DIR) != 0;
         }
 
-        public final void setIsModuleDir() {
-            flags |= MODULE_DIR;
-        }
-
-        public final boolean isModuleDir() {
-            return (flags & MODULE_DIR) != 0;
-        }
-
-        public final void setIsMetaInfDir() {
-            flags |= METAINF_DIR;
-        }
-
-        public final boolean isMetaInfDir() {
-            return (flags & METAINF_DIR) != 0;
+        public final void setIsPackagesDir() {
+            flags |= PACKAGES_DIR;
         }
 
-        public final void setIsTopLevelPackageDir() {
-            flags |= TOPLEVEL_PKG_DIR;
-        }
-
-        public final boolean isTopLevelPackageDir() {
-            return (flags & TOPLEVEL_PKG_DIR) != 0;
+        public final boolean isPackagesDir() {
+            return (flags & PACKAGES_DIR) != 0;
         }
 
-        public final void setIsHidden() {
-            flags |= HIDDEN;
+        public final void setIsModulesDir() {
+            flags |= MODULES_DIR;
         }
 
-        public final boolean isHidden() {
-            return (flags & HIDDEN) != 0;
-        }
-
-        public final boolean isVisible() {
-            return !isHidden();
+        public final boolean isModulesDir() {
+            return (flags & MODULES_DIR) != 0;
         }
 
         public final UTF8String getName() {
@@ -169,6 +191,20 @@
             return fileAttrs;
         }
 
+        // resolve this Node (if this is a soft link, get underlying Node)
+        public final Node resolveLink() {
+            return resolveLink(false);
+        }
+
+        public Node resolveLink(boolean recursive) {
+            return this;
+        }
+
+        // is this a soft link Node?
+        public boolean isLink() {
+            return false;
+        }
+
         public boolean isDirectory() {
             return false;
         }
@@ -242,16 +278,20 @@
     }
 
     // directory node - directory has full path name without '/' at end.
-    public static final class Directory extends Node {
+    static final class Directory extends Node {
         private final List<Node> children;
 
-        @SuppressWarnings("LeakingThisInConstructor")
-        Directory(Directory parent, UTF8String name, BasicFileAttributes fileAttrs) {
+        private Directory(Directory parent, UTF8String name, BasicFileAttributes fileAttrs) {
             super(name, fileAttrs);
             children = new ArrayList<>();
+        }
+
+        static Directory create(Directory parent, UTF8String name, BasicFileAttributes fileAttrs) {
+            Directory dir = new Directory(parent, name, fileAttrs);
             if (parent != null) {
-                parent.addChild(this);
+                parent.addChild(dir);
             }
+            return dir;
         }
 
         @Override
@@ -259,6 +299,7 @@
             return true;
         }
 
+        @Override
         public List<Node> getChildren() {
             return Collections.unmodifiableList(children);
         }
@@ -281,19 +322,33 @@
 
     // "resource" is .class or any other resource (compressed/uncompressed) in a jimage.
     // full path of the resource is the "name" of the resource.
-    public static class Resource extends Node {
+    static class Resource extends Node {
         private final ImageLocation loc;
 
-        @SuppressWarnings("LeakingThisInConstructor")
-        Resource(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) {
-            this(parent, ROOT.concat(loc.getFullname()), loc, fileAttrs);
+        private Resource(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) {
+            this(parent, loc.getFullName(true), loc, fileAttrs);
         }
 
-        @SuppressWarnings("LeakingThisInConstructor")
-        Resource(Directory parent, UTF8String name, ImageLocation loc, BasicFileAttributes fileAttrs) {
+        private Resource(Directory parent, UTF8String name, ImageLocation loc, BasicFileAttributes fileAttrs) {
             super(name, fileAttrs);
             this.loc = loc;
-            parent.addChild(this);
+         }
+
+        static Resource create(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) {
+            Resource resource = new Resource(parent, loc, fileAttrs);
+            parent.addChild(resource);
+            return resource;
+        }
+
+        static Resource create(Directory parent, UTF8String name, ImageLocation loc, BasicFileAttributes fileAttrs) {
+            Resource resource = new Resource(parent, name, loc, fileAttrs);
+            parent.addChild(resource);
+            return resource;
+        }
+
+        @Override
+        public boolean isCompleted() {
+            return true;
         }
 
         @Override
@@ -327,6 +382,37 @@
         }
     }
 
+    // represents a soft link to another Node
+    static class LinkNode extends Node {
+        private final Node link;
+
+        private LinkNode(Directory parent, UTF8String name, Node link) {
+            super(name, link.getFileAttributes());
+            this.link = link;
+        }
+
+        static LinkNode create(Directory parent, UTF8String name, Node link) {
+            LinkNode linkNode = new LinkNode(parent, name, link);
+            parent.addChild(linkNode);
+            return linkNode;
+        }
+
+        @Override
+        public boolean isCompleted() {
+            return true;
+        }
+
+        @Override
+        public Node resolveLink(boolean recursive) {
+            return recursive && (link instanceof LinkNode)? ((LinkNode)link).resolveLink(true) : link;
+        }
+
+        @Override
+        public boolean isLink() {
+            return true;
+        }
+    }
+
     // directory management interface
     public Directory getRootDirectory() {
         return buildRootDirectory();
@@ -340,9 +426,154 @@
         return findNode(new UTF8String(name));
     }
 
+    /**
+     * To visit sub tree resources.
+     */
+    interface LocationVisitor {
+
+        void visit(ImageLocation loc);
+    }
+
+    /**
+     * Lazily build a node from a name.
+     */
+    private final class NodeBuilder {
+
+        private static final int SIZE_OF_OFFSET = 4;
+
+        private final UTF8String name;
+
+        private NodeBuilder(UTF8String name) {
+            this.name = name;
+        }
+
+        private Node buildNode() {
+            Node n = null;
+            boolean isPackages = false;
+            boolean isModules = false;
+            String strName = name.toString();
+            if (strName.startsWith("" + PACKAGES_STRING)) {
+                isPackages = true;
+            } else {
+                if (strName.startsWith("" + MODULES_STRING)) {
+                    isModules = true;
+                }
+            }
+            if (!isModules && !isPackages) {
+                return null;
+            }
+
+            ImageLocation loc = findLocation(name);
+
+            if (loc != null) { // A sub tree node
+                if (isPackages) {
+                    n = handlePackages(strName, loc);
+                } else { // modules sub tree
+                    n = handleModulesSubTree(strName, loc);
+                }
+            } else { // Asking for a resource? /modules/java.base/java/lang/Object.class
+                if (isModules) {
+                    n = handleResource(strName, loc);
+                }
+            }
+            return n;
+        }
+
+        private void visitLocation(ImageLocation loc, LocationVisitor visitor) {
+            byte[] offsets = getResource(loc);
+            ByteBuffer buffer = ByteBuffer.wrap(offsets);
+            buffer.order(getByteOrder());
+            IntBuffer intBuffer = buffer.asIntBuffer();
+            for (int i = 0; i < offsets.length / SIZE_OF_OFFSET; i++) {
+                int offset = intBuffer.get(i);
+                ImageLocation pkgLoc = getLocation(offset);
+                visitor.visit(pkgLoc);
+            }
+        }
+
+        private Node handlePackages(String name, ImageLocation loc) {
+            long size = loc.getUncompressedSize();
+            Node n = null;
+            // Only possiblities are /packages, /packages/package/module
+            if (name.equals("" + PACKAGES_STRING)) {
+                visitLocation(loc, (childloc) -> {
+                    findNode(childloc.getFullName());
+                });
+                packagesDir.setCompleted(true);
+                n = packagesDir;
+            } else {
+                if (size != 0) { // children are links to module
+                    String pkgName = getBaseExt(loc);
+                    Directory pkgDir = newDirectory(packagesDir,
+                            packagesDir.getName().concat(SLASH_STRING, new UTF8String(pkgName)));
+                    visitLocation(loc, (childloc) -> {
+                        findNode(childloc.getFullName());
+                    });
+                    pkgDir.setCompleted(true);
+                    n = pkgDir;
+                } else { // Link to module
+                    String pkgName = loc.getParentString();
+                    String modName = getBaseExt(loc);
+                    Node targetNode = findNode(MODULES_STRING + "/" + modName);
+                    if (targetNode != null) {
+                        UTF8String pkgDirName = packagesDir.getName().concat(SLASH_STRING, new UTF8String(pkgName));
+                        Directory pkgDir = (Directory) nodes.get(pkgDirName);
+                        Node linkNode = newLinkNode(pkgDir,
+                                pkgDir.getName().concat(SLASH_STRING, new UTF8String(modName)), targetNode);
+                        n = linkNode;
+                    }
+                }
+            }
+            return n;
+        }
+
+        private Node handleModulesSubTree(String name, ImageLocation loc) {
+            Node n;
+            Directory dir = makeDirectories(loc.getFullName());
+            visitLocation(loc, (childloc) -> {
+                String path = childloc.getFullNameString();
+                if (path.startsWith(MODULES_STRING.toString())) { // a package
+                    makeDirectories(childloc.getFullName());
+                } else { // a resource
+                    makeDirectories(childloc.buildName(true, true, false));
+                    newResource(dir, childloc);
+                }
+            });
+            dir.setCompleted(true);
+            n = dir;
+            return n;
+        }
+
+        private Node handleResource(String name, ImageLocation loc) {
+            Node n = null;
+            String locationPath = name.substring((MODULES_STRING).length());
+            ImageLocation resourceLoc = findLocation(locationPath);
+            if (resourceLoc != null) {
+                Directory dir = makeDirectories(resourceLoc.buildName(true, true, false));
+                Resource res = newResource(dir, resourceLoc);
+                n = res;
+            }
+            return n;
+        }
+
+        private String getBaseExt(ImageLocation loc) {
+            String base = loc.getBaseString();
+            String ext = loc.getExtensionString();
+            if (ext != null && !ext.isEmpty()) {
+                base = base + "." + ext;
+            }
+            return base;
+        }
+    }
+
     public synchronized Node findNode(UTF8String name) {
         buildRootDirectory();
-        return nodes.get(name);
+        Node n = nodes.get(name);
+        if (n == null || !n.isCompleted()) {
+            NodeBuilder builder = new NodeBuilder(name);
+            n = builder.buildNode();
+        }
+        return n;
     }
 
     private synchronized void clearNodes() {
@@ -375,65 +606,61 @@
         // FIXME no time information per resource in jimage file (yet?)
         // we use file attributes of jimage itself.
         // root directory
-        rootDir = new Directory(null, ROOT, imageFileAttributes());
+        rootDir = newDirectory(null, ROOT_STRING);
         rootDir.setIsRootDir();
-        nodes.put(rootDir.getName(), rootDir);
 
-        ImageLocation[] locs = getAllLocations(true);
-        for (ImageLocation loc : locs) {
-            UTF8String parent = loc.getParent();
-            // directory where this location goes as child
-            Directory dir;
-            if (parent == null || parent.isEmpty()) {
-                // top level entry under root
-                dir = rootDir;
-            } else {
-                int idx = parent.lastIndexOf('/');
-                assert idx != -1 : "invalid parent string";
-                UTF8String name = ROOT.concat(parent.substring(0, idx));
-                dir = (Directory) nodes.get(name);
-                if (dir == null) {
-                    // make all parent directories (as needed)
-                    dir = makeDirectories(parent);
-                }
-            }
-            Resource entry = new Resource(dir, loc, imageFileAttributes());
-            nodes.put(entry.getName(), entry);
-        }
+        // /packages dir
+        packagesDir = newDirectory(rootDir, PACKAGES_STRING);
+        packagesDir.setIsPackagesDir();
 
-        Node metaInf = nodes.get(META_INF);
-        if (metaInf instanceof Directory) {
-            metaInf.setIsMetaInfDir();
-            ((Directory)metaInf).walk(Node::setIsHidden);
-        }
+        // /modules dir
+        modulesDir = newDirectory(rootDir, MODULES_STRING);
+        modulesDir.setIsModulesDir();
 
-        fillPackageModuleInfo();
-
+        rootDir.setCompleted(true);
         return rootDir;
     }
 
     private Directory newDirectory(Directory parent, UTF8String name) {
-        Directory dir = new Directory(parent, name, imageFileAttributes());
+        Directory dir = Directory.create(parent, name, imageFileAttributes());
         nodes.put(dir.getName(), dir);
         return dir;
     }
 
-    private Directory makeDirectories(UTF8String parent) {
-        assert !parent.isEmpty() : "non empty parent expected";
+    private Resource newResource(Directory parent, ImageLocation loc) {
+        Resource res = Resource.create(parent, loc, imageFileAttributes());
+        nodes.put(res.getName(), res);
+        return res;
+    }
+
+    private LinkNode newLinkNode(Directory dir, UTF8String name, Node link) {
+        LinkNode linkNode = LinkNode.create(dir, name, link);
+        nodes.put(linkNode.getName(), linkNode);
+        return linkNode;
+    }
+
+    private List<UTF8String> dirs(UTF8String parent) {
+        List<UTF8String> splits = new ArrayList<>();
 
-        int idx = parent.indexOf('/');
-        assert idx != -1 : "invalid parent string";
-        UTF8String name = ROOT.concat(parent.substring(0, idx));
-        Directory top = (Directory) nodes.get(name);
-        if (top == null) {
-            top = newDirectory(rootDir, name);
+        for (int i = 1; i < parent.length(); i++) {
+            if (parent.byteAt(i) == '/') {
+                splits.add(parent.substring(0, i));
+            }
         }
-        Directory last = top;
-        while ((idx = parent.indexOf('/', idx + 1)) != -1) {
-            name = ROOT.concat(parent.substring(0, idx));
-            Directory nextDir = (Directory) nodes.get(name);
+
+        splits.add(parent);
+
+        return splits;
+    }
+
+    private Directory makeDirectories(UTF8String parent) {
+        Directory last = rootDir;
+        List<UTF8String> dirs = dirs(parent);
+
+        for (UTF8String dir : dirs) {
+            Directory nextDir = (Directory) nodes.get(dir);
             if (nextDir == null) {
-                nextDir = newDirectory(last, name);
+                nextDir = newDirectory(last, dir);
             }
             last = nextDir;
         }
@@ -441,54 +668,6 @@
         return last;
     }
 
-    private void fillPackageModuleInfo() {
-        assert rootDir != null;
-
-        packageMap.entrySet().stream().sorted((x, y)->x.getKey().compareTo(y.getKey())).forEach((entry) -> {
-              UTF8String moduleName = new UTF8String("/" + entry.getValue());
-              UTF8String fullName = moduleName.concat(new UTF8String(entry.getKey() + "/"));
-              if (! nodes.containsKey(fullName)) {
-                  Directory module = (Directory) nodes.get(moduleName);
-                  assert module != null : "module directory missing " + moduleName;
-                  module.setIsModuleDir();
-
-                  // hide "packages.offsets" in module directories
-                  Node packagesOffsets = nodes.get(moduleName.concat(ROOT, PACKAGES_OFFSETS));
-                  if (packagesOffsets != null) {
-                      packagesOffsets.setIsHidden();
-                  }
-
-                  // package name without front '/'
-                  UTF8String pkgName = new UTF8String(entry.getKey() + "/");
-                  int idx = -1;
-                  Directory moduleSubDir = module;
-                  while ((idx = pkgName.indexOf('/', idx + 1)) != -1) {
-                      UTF8String subPkg = pkgName.substring(0, idx);
-                      UTF8String moduleSubDirName = moduleName.concat(ROOT, subPkg);
-                      Directory tmp = (Directory) nodes.get(moduleSubDirName);
-                      if (tmp == null) {
-                          moduleSubDir = newDirectory(moduleSubDir, moduleSubDirName);
-                      } else {
-                          moduleSubDir = tmp;
-                      }
-                  }
-                  // copy pkgDir "resources"
-                  Directory pkgDir = (Directory) nodes.get(ROOT.concat(pkgName.substring(0, pkgName.length() - 1)));
-                  pkgDir.setIsTopLevelPackageDir();
-                  pkgDir.walk(n -> n.setIsHidden());
-                  for (Node child : pkgDir.getChildren()) {
-                      if (child.isResource()) {
-                          ImageLocation loc = child.getLocation();
-                          BasicFileAttributes imageFileAttrs = child.getFileAttributes();
-                          UTF8String rsName = moduleName.concat(child.getName());
-                          Resource rs = new Resource(moduleSubDir, rsName, loc, imageFileAttrs);
-                          nodes.put(rs.getName(), rs);
-                      }
-                  }
-              }
-        });
-    }
-
     public byte[] getResource(Node node) throws IOException {
         if (node.isResource()) {
             return super.getResource(node.getLocation());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageReaderFactory.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jimage;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map;
+
+/**
+ * Factory to get ImageReader
+ */
+public class ImageReaderFactory {
+    private ImageReaderFactory() {}
+
+    private static final String JAVA_HOME = System.getProperty("java.home");
+    private static final Path BOOT_MODULES_JIMAGE =
+        Paths.get(JAVA_HOME, "lib", "modules", "bootmodules.jimage");
+
+    private static final Map<Path, ImageReader> readers = new ConcurrentHashMap<>();
+
+    /**
+     * Returns an {@code ImageReader} to read from the given image file
+     */
+    public static ImageReader get(Path jimage) throws IOException {
+        ImageReader reader = readers.get(jimage);
+        if (reader != null) {
+            return reader;
+        }
+        reader = ImageReader.open(jimage.toString());
+        // potential race with other threads opening the same URL
+        ImageReader r = readers.putIfAbsent(jimage, reader);
+        if (r == null) {
+            return reader;
+        } else {
+            reader.close();
+            return r;
+        }
+    }
+
+    /**
+     * Returns the {@code ImageReader} to read the image file in this
+     * run-time image.
+     *
+     * @throws UncheckedIOException if an I/O error occurs
+     */
+    public static ImageReader getImageReader() {
+        try {
+            return get(BOOT_MODULES_JIMAGE);
+        } catch (IOException ioe) {
+            throw new UncheckedIOException(ioe);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageResourcesTree.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,344 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * A class to build a sorted tree of Resource paths as a tree of ImageLocation.
+ *
+ */
+// XXX Public only due to the JImageTask / JImageTask code duplication
+public final class ImageResourcesTree {
+
+    private static final String MODULES = "modules";
+    private static final String PACKAGES = "packages";
+    public static final String MODULES_STRING = UTF8String.MODULES_STRING.toString();
+    public static final String PACKAGES_STRING = UTF8String.PACKAGES_STRING.toString();
+
+    public static boolean isTreeInfoResource(String path) {
+        return path.startsWith(PACKAGES_STRING) || path.startsWith(MODULES_STRING);
+    }
+
+    /**
+     * Path item tree node.
+     */
+    private static final class Node {
+
+        private final String name;
+        private final Map<String, Node> children = new TreeMap<>();
+        private final Node parent;
+        private ImageLocationWriter loc;
+
+        private Node(String name, Node parent) {
+            this.name = name;
+            this.parent = parent;
+
+            if (parent != null) {
+                parent.children.put(name, this);
+            }
+        }
+
+        public String getPath() {
+            if (parent == null) {
+                return "/";
+            }
+            return buildPath(this);
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Node getChildren(String name) {
+            Node item = children.get(name);
+            return item;
+        }
+
+        private static String buildPath(Node item) {
+            if (item == null) {
+                return null;
+            }
+            String path = buildPath(item.parent);
+            if (path == null) {
+                return item.getName();
+            } else {
+                return path + "/" + item.getName();
+            }
+        }
+    }
+
+    /**
+     * Tree of nodes.
+     */
+    private static final class Tree {
+
+        private final Map<String, Node> directAccess = new HashMap<>();
+        private final List<String> paths;
+        private final Node root;
+        private Node modules;
+        private Node packages;
+
+        private Tree(List<String> paths) {
+            this.paths = paths;
+            root = new Node("", null);
+            buildTree();
+        }
+
+        private void buildTree() {
+            modules = new Node(MODULES, root);
+            directAccess.put(modules.getPath(), modules);
+
+            Map<String, Set<String>> moduleToPackage = new TreeMap<>();
+            Map<String, Set<String>> packageToModule = new TreeMap<>();
+
+            for (String p : paths) {
+                if (!p.startsWith("/")) {
+                    continue;
+                }
+                String[] split = p.split("/");
+                Node current = modules;
+                String module = null;
+                for (int i = 0; i < split.length; i++) {
+                    String s = split[i];
+                    if (!s.isEmpty()) {
+                        if (module == null) {
+                            module = s;
+                        }
+                        Node n = current.children.get(s);
+                        if (n == null) {
+                            n = new Node(s, current);
+                            if (i == split.length - 1) { // Leaf
+                                String pkg = toPackageName(n.parent);
+                                if (pkg != null && !pkg.startsWith("META-INF")) {
+                                    Set<String> pkgs = moduleToPackage.get(module);
+                                    if (pkgs == null) {
+                                        pkgs = new TreeSet<>();
+                                        moduleToPackage.put(module, pkgs);
+                                    }
+                                    pkgs.add(pkg);
+                                }
+                            } else { // put only sub trees, no leaf
+                                directAccess.put(n.getPath(), n);
+                                String pkg = toPackageName(n);
+                                if (pkg != null && !pkg.startsWith("META-INF")) {
+                                    Set<String> mods = packageToModule.get(pkg);
+                                    if (mods == null) {
+                                        mods = new TreeSet<>();
+                                        packageToModule.put(pkg, mods);
+                                    }
+                                    mods.add(module);
+
+                                }
+                            }
+                        }
+                        current = n;
+                    }
+                }
+            }
+            packages = new Node(PACKAGES, root);
+            directAccess.put(packages.getPath(), packages);
+            for (Map.Entry<String, Set<String>> entry : moduleToPackage.entrySet()) {
+                for (String pkg : entry.getValue()) {
+                    Node pkgNode = new Node(pkg, packages);
+                    directAccess.put(pkgNode.getPath(), pkgNode);
+
+                    Node modNode = new Node(entry.getKey(), pkgNode);
+                    directAccess.put(modNode.getPath(), modNode);
+                }
+            }
+            for (Map.Entry<String, Set<String>> entry : packageToModule.entrySet()) {
+                Node pkgNode = new Node(entry.getKey(), packages);
+                directAccess.put(pkgNode.getPath(), pkgNode);
+                for (String module : entry.getValue()) {
+                    Node modNode = new Node(module, pkgNode);
+                    directAccess.put(modNode.getPath(), modNode);
+                }
+            }
+        }
+
+        public String toResourceName(Node node) {
+            if (!node.children.isEmpty()) {
+                throw new RuntimeException("Node is not a resource");
+            }
+            return removeRadical(node);
+        }
+
+        public String getModule(Node node) {
+            if (node.parent == null || node.getName().equals(MODULES) ||
+                node.getName().startsWith(PACKAGES)) {
+                return null;
+            }
+            String path = removeRadical(node);
+            // "/xxx/...";
+            path = path.substring(1);
+            int i = path.indexOf("/");
+            if (i == -1) {
+                return path;
+            } else {
+                return path.substring(0, i);
+            }
+        }
+
+        public String toPackageName(Node node) {
+            if (node.parent == null) {
+                return null;
+            }
+            String path = removeRadical(node.getPath(), "/" + MODULES + "/");
+            String module = getModule(node);
+            if (path.equals(module)) {
+                return null;
+            }
+            String pkg = removeRadical(path, module + "/");
+            return pkg.replaceAll("/", ".");
+        }
+
+        public String removeRadical(Node node) {
+            String s = node.getPath();
+            return removeRadical(node.getPath(), "/" + MODULES);
+        }
+
+        private String removeRadical(String path, String str) {
+            return path.substring(str.length());
+        }
+
+        public Node getRoot() {
+            return root;
+        }
+
+        public Map<String, Node> getMap() {
+            return directAccess;
+        }
+
+        private boolean isPackageNode(Node node) {
+            if (!node.children.isEmpty()) {
+                throw new RuntimeException("Node is not a package");
+            }
+            return node.getPath().startsWith("/" + PACKAGES);
+        }
+    }
+
+    private static final class LocationsAdder {
+
+        private long offset;
+        private final List<byte[]> content = new ArrayList<>();
+        private final BasicImageWriter writer;
+        private final Tree tree;
+
+        LocationsAdder(Tree tree, long offset, BasicImageWriter writer) {
+            this.tree = tree;
+            this.offset = offset;
+            this.writer = writer;
+            addLocations(tree.getRoot());
+        }
+
+        private int addLocations(Node current) {
+            int[] ret = new int[current.children.size()];
+            int i = 0;
+            for (java.util.Map.Entry<String, Node> entry : current.children.entrySet()) {
+                ret[i] = addLocations(entry.getValue());
+                i += 1;
+            }
+            if (current != tree.getRoot() && (ret.length > 0 || tree.isPackageNode(current))) {
+                int size = ret.length * 4;
+                writer.addLocation(current.getPath(), offset, 0, size);
+                offset += size;
+            }
+            return 0;
+        }
+
+        private List<byte[]> computeContent() {
+            // Map used to associate Tree item with locations offset.
+            Map<String, ImageLocationWriter> outLocations = new HashMap<>();
+            for (ImageLocationWriter wr : writer.getLocations()) {
+                outLocations.put(wr.getFullNameString(), wr);
+            }
+            // Attach location to node
+            for (Map.Entry<String, ImageLocationWriter> entry : outLocations.entrySet()) {
+                Node item = tree.getMap().get(entry.getKey());
+                if (item != null) {
+                    item.loc = entry.getValue();
+                }
+            }
+            computeContent(tree.getRoot(), outLocations);
+            return content;
+        }
+
+        private int computeContent(Node current, Map<String, ImageLocationWriter> outLocations) {
+            int[] ret = new int[current.children.size()];
+            int i = 0;
+            for (java.util.Map.Entry<String, Node> entry : current.children.entrySet()) {
+                ret[i] = computeContent(entry.getValue(), outLocations);
+                i += 1;
+            }
+            if (ret.length > 0) {
+                int size = ret.length * 4;
+                ByteBuffer buff = ByteBuffer.allocate(size);
+                buff.order(writer.getByteOrder());
+                for (int val : ret) {
+                    buff.putInt(val);
+                }
+                byte[] arr = buff.array();
+                content.add(arr);
+            } else {
+                if (tree.isPackageNode(current)) {
+                    current.loc = outLocations.get(current.getPath());
+                } else {
+                    String s = tree.toResourceName(current);
+                    current.loc = outLocations.get(s);
+                }
+            }
+            return current == tree.getRoot() ? 0 : current.loc.getLocationOffset();
+        }
+    }
+
+    private final List<String> paths;
+    private final LocationsAdder adder;
+
+    public ImageResourcesTree(long offset, BasicImageWriter writer, List<String> paths) {
+        this.paths = new ArrayList<>();
+        this.paths.addAll(paths);
+        Collections.sort(this.paths);
+        Tree tree = new Tree(this.paths);
+        adder = new LocationsAdder(tree, offset, writer);
+    }
+
+    public void addContent(DataOutputStream out) throws IOException {
+        List<byte[]> content = adder.computeContent();
+        for (byte[] c : content) {
+            out.write(c, 0, c.length);
+        }
+    }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageStream.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageStream.java	Thu Jul 09 22:46:18 2015 -0700
@@ -72,7 +72,7 @@
         return this;
     }
 
-    private void ensure(int needs) {
+    void ensure(int needs) {
         assert 0 <= needs : "Negative needs";
 
         if (needs > buffer.remaining()) {
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageStrings.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageStrings.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,83 +25,8 @@
 
 package jdk.internal.jimage;
 
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-
-class ImageStrings {
-    private static final int NOT_FOUND = -1;
-    static final int EMPTY_OFFSET = 0;
-
-    private final HashMap<UTF8String, Integer> stringToOffsetMap;
-    private final ImageStream stream;
-
-    ImageStrings() {
-        this.stringToOffsetMap = new HashMap<>();
-        this.stream = new ImageStream();
-
-        // Reserve 0 offset for empty string.
-        int offset = addString(UTF8String.EMPTY_STRING);
-        assert offset == 0 : "Empty string not zero offset";
-        // Reserve 1 offset for frequently used ".class".
-        addString(UTF8String.CLASS_STRING);
-    }
-
-    ImageStrings(ImageStream stream) {
-        this.stringToOffsetMap = new HashMap<>();
-        this.stream = stream;
-    }
-
-    private int addString(final UTF8String string) {
-        int offset = stream.getPosition();
-        string.writeTo(stream);
-        stream.put('\0');
-        stringToOffsetMap.put(string, offset);
-
-        return offset;
-    }
-
-    int add(final UTF8String string) {
-        int offset = find(string);
+interface ImageStrings {
+    public UTF8String get(int offset);
 
-        return offset == NOT_FOUND ? addString(string) : offset;
-    }
-
-    int find(final UTF8String string) {
-        Integer offset = stringToOffsetMap.get(string);
-
-        return offset != null ? offset : NOT_FOUND;
-    }
-
-    UTF8String get(int offset) {
-        ByteBuffer buffer = stream.getBuffer();
-        assert 0 <= offset && offset < buffer.capacity() : "String buffer offset out of range";
-        int zero = NOT_FOUND;
-        for (int i = offset; i < buffer.capacity(); i++) {
-            if (buffer.get(i) == '\0') {
-                zero = i;
-                break;
-            }
-        }
-        assert zero != UTF8String.NOT_FOUND;
-        int length = zero - offset;
-        byte[] bytes = new byte[length];
-        int mark = buffer.position();
-        buffer.position(offset);
-        buffer.get(bytes);
-        buffer.position(mark);
-
-        return new UTF8String(bytes, 0, length);
-    }
-
-    ImageStream getStream() {
-        return stream;
-    }
-
-    int getSize() {
-        return stream.getSize();
-    }
-
-    int getCount() {
-        return stringToOffsetMap.size();
-    }
+    public int add(final UTF8String string);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,44 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jimage;
+
+class ImageStringsReader implements ImageStrings {
+    private final BasicImageReader reader;
+
+    ImageStringsReader(BasicImageReader reader) {
+        this.reader = reader;
+    }
+
+    @Override
+    public UTF8String get(int offset) {
+        return reader.getUTF8String(offset);
+    }
+
+    @Override
+    public int add(final UTF8String string) {
+        throw new InternalError("Can not add strings at runtime");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageStringsWriter.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,105 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.jimage;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+
+class ImageStringsWriter implements ImageStrings {
+    private static final int NOT_FOUND = -1;
+    static final int EMPTY_OFFSET = 0;
+    static final UTF8String CLASS_STRING = new UTF8String("class");
+
+    private final HashMap<UTF8String, Integer> stringToOffsetMap;
+    private final ImageStream stream;
+
+    ImageStringsWriter() {
+        this.stringToOffsetMap = new HashMap<>();
+        this.stream = new ImageStream();
+
+        // Reserve 0 offset for empty string.
+        int offset = addString(UTF8String.EMPTY_STRING);
+        assert offset == 0 : "Empty string not zero offset";
+        // Reserve 1 offset for frequently used ".class".
+        addString(CLASS_STRING);
+    }
+
+    private int addString(final UTF8String string) {
+        int offset = stream.getPosition();
+        string.writeTo(stream);
+        stream.put('\0');
+        stringToOffsetMap.put(string, offset);
+
+        return offset;
+    }
+
+    @Override
+    public int add(final UTF8String string) {
+        int offset = find(string);
+
+        return offset == NOT_FOUND ? addString(string) : offset;
+    }
+
+    int find(final UTF8String string) {
+        Integer offset = stringToOffsetMap.get(string);
+
+        return offset != null ? offset : NOT_FOUND;
+    }
+
+    @Override
+    public UTF8String get(int offset) {
+        ByteBuffer buffer = stream.getBuffer();
+        assert 0 <= offset && offset < buffer.capacity() : "String buffer offset out of range";
+        int zero = NOT_FOUND;
+        for (int i = offset; i < buffer.capacity(); i++) {
+            if (buffer.get(i) == '\0') {
+                zero = i;
+                break;
+            }
+        }
+        assert zero != UTF8String.NOT_FOUND;
+        int length = zero - offset;
+        byte[] bytes = new byte[length];
+        int mark = buffer.position();
+        buffer.position(offset);
+        buffer.get(bytes);
+        buffer.position(mark);
+
+        return new UTF8String(bytes, 0, length);
+    }
+
+    ImageStream getStream() {
+        return stream;
+    }
+
+    int getSize() {
+        return stream.getSize();
+    }
+
+    int getCount() {
+        return stringToOffsetMap.size();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageSubstrate.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,45 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage;
+
+import java.io.Closeable;
+import java.nio.ByteBuffer;
+
+interface ImageSubstrate extends Closeable {
+    @Override
+    void close();
+    boolean supportsDataBuffer();
+    ByteBuffer getIndexBuffer(long offset, long size);
+    ByteBuffer getDataBuffer(long offset, long size);
+    boolean read(long offset,
+                          ByteBuffer compressedBuffer, long compressedSize,
+                          ByteBuffer uncompressedBuffer, long uncompressedSize);
+    boolean read(long offset,
+                          ByteBuffer uncompressedBuffer, long uncompressedSize);
+    byte[] getStringBytes(int offset);
+    long[] getAttributes(int offset);
+    ImageLocation findLocation(UTF8String name, ImageStringsReader strings);
+    int[] attributeOffsets();
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/PReader.java	Wed Jul 05 20:41:30 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.internal.jimage;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileChannel;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * Supports reading a file from given positions (offsets) in the file.
- */
-
-public abstract class PReader implements Closeable {
-    private final FileChannel fc;
-
-    protected PReader(FileChannel fc) {
-        this.fc = fc;
-    }
-
-    /**
-     * Returns the {@code FileChannel}.
-     */
-    final FileChannel channel() {
-        return fc;
-    }
-
-    /**
-     * Closes this {@code PReader} and the underlying file.
-     */
-    @Override
-    public final void close() throws IOException {
-        fc.close();
-    }
-
-    /**
-     * Returns {@code true} if this {@code PReader} and the underlying file is
-     * open.
-     */
-    public final boolean isOpen() {
-        return fc.isOpen();
-    }
-
-    /**
-     * Returns {@code len} bytes from a given position in the file. The bytes
-     * are returned as a byte array.
-     *
-     * @throws IOException if an I/O error occurs
-     */
-    public abstract byte[] read(int len, long position) throws IOException;
-
-    /**
-     * Opens the given file, returning a {@code PReader} to read from the file.
-     *
-     * @implNote Returns a {@code PReader} that supports concurrent pread operations
-     * if possible, otherwise a simple {@code PReader} that doesn't support
-     * concurrent operations.
-     */
-    static PReader open(String file) throws IOException {
-        Class<?> clazz;
-        try {
-            clazz = Class.forName("jdk.internal.jimage.concurrent.ConcurrentPReader");
-        } catch (ClassNotFoundException e) {
-            return new SimplePReader(file);
-        }
-        try {
-            Constructor<?> ctor = clazz.getConstructor(String.class);
-            return (PReader) ctor.newInstance(file);
-        } catch (InvocationTargetException e) {
-            Throwable cause = e.getCause();
-            if (cause instanceof IOException)
-                throw (IOException) cause;
-            if (cause instanceof Error)
-                throw (Error) cause;
-            if (cause instanceof RuntimeException)
-                throw (RuntimeException) cause;
-            throw new Error(e);
-        } catch (NoSuchMethodException | IllegalAccessException |
-                InstantiationException e) {
-            throw new InternalError(e);
-        }
-    }
-}
-
-/**
- * Simple PReader implementation based on {@code RandomAccessFile}.
- *
- * @implNote This class cannot use FileChannel read methods to do the
- * positional reads because FileChannel is interruptible.
- */
-class SimplePReader extends PReader {
-    private final RandomAccessFile raf;
-
-    private SimplePReader(RandomAccessFile raf) throws IOException {
-        super(raf.getChannel());
-        this.raf = raf;
-    }
-
-    SimplePReader(String file) throws IOException {
-        this(new RandomAccessFile(file, "r"));
-    }
-
-    @Override
-    public byte[] read(int len, long position) throws IOException {
-        synchronized (this) {
-            byte[] bytes = new byte[len];
-            raf.seek(position);
-            int n = raf.read(bytes);
-            if (n != len)
-                throw new InternalError("short read, not handled yet");
-            return bytes;
-        }
-    }
-}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/PackageModuleMap.java	Wed Jul 05 20:41:30 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.internal.jimage;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-// Utility to read module info from .jimage file.
-
-public final class PackageModuleMap {
-    private PackageModuleMap() {}
-
-    public static final String MODULES_ENTRY = "module/modules.offsets";
-    public static final String PACKAGES_ENTRY = "packages.offsets";
-
-    /*
-     * Returns a package-to-module map.
-     *
-     * The package name is in binary name format.
-     */
-    static Map<String,String> readFrom(ImageReader reader) throws IOException {
-        Map<String,String> result = new HashMap<>();
-        List<String> moduleNames = reader.getNames(MODULES_ENTRY);
-
-        for (String moduleName : moduleNames) {
-            List<String> packageNames = reader.getNames(moduleName + "/" + PACKAGES_ENTRY);
-
-            for (String packageName : packageNames) {
-                result.put(packageName, moduleName);
-            }
-        }
-        return result;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/PerfectHashBuilder.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,255 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+*/
+
+package jdk.internal.jimage;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class PerfectHashBuilder<E> {
+    private final static int RETRY_LIMIT = 1000;
+
+    private Class<?> entryComponent;
+    private Class<?> bucketComponent;
+
+    private final Map<UTF8String, Entry<E>> map = new LinkedHashMap<>();
+    private int[] redirect;
+    private Entry<E>[] order;
+    private int count = 0;
+
+    @SuppressWarnings("EqualsAndHashcode")
+    public static class Entry<E> {
+        private final UTF8String key;
+        private final E value;
+
+        Entry() {
+            this("", null);
+        }
+
+        Entry(String key, E value) {
+            this(new UTF8String(key), value);
+        }
+
+        Entry(UTF8String key, E value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        UTF8String getKey() {
+            return key;
+        }
+
+        E getValue() {
+            return value;
+        }
+
+        int hashCode(int seed) {
+            return key.hashCode(seed);
+        }
+
+        @Override
+        public int hashCode() {
+            return key.hashCode();
+        }
+    }
+
+    static class Bucket<E> implements Comparable<Bucket<E>> {
+        final List<Entry<E>> list = new ArrayList<>();
+
+        void add(Entry<E> entry) {
+            list.add(entry);
+        }
+
+        int getSize() {
+            return list.size();
+        }
+
+        List<Entry<E>> getList() {
+            return list;
+        }
+
+        Entry<E> getFirst() {
+            assert !list.isEmpty() : "bucket should never be empty";
+            return list.get(0);
+        }
+
+        @Override
+        public int hashCode() {
+            return getFirst().hashCode();
+        }
+
+        @Override
+        @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
+        public boolean equals(Object obj) {
+            return this == obj;
+        }
+
+        @Override
+        public int compareTo(Bucket<E> o) {
+            return o.getSize() - getSize();
+        }
+    }
+
+    public PerfectHashBuilder(Class<?> entryComponent, Class<?> bucketComponent) {
+        this.entryComponent = entryComponent;
+        this.bucketComponent = bucketComponent;
+    }
+
+    public int getCount() {
+        return map.size();
+    }
+
+    public int[] getRedirect() {
+        return redirect;
+    }
+
+    public Entry<E>[] getOrder() {
+        return order;
+    }
+
+    public Entry<E> put(String key, E value) {
+        return put(new UTF8String(key), value);
+    }
+
+    public Entry<E> put(UTF8String key, E value) {
+        return put(new Entry<>(key, value));
+    }
+
+    public Entry<E> put(Entry<E> entry) {
+        Entry<E> old = map.put(entry.key, entry);
+
+        if (old == null) {
+            count++;
+        }
+
+        return old;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void generate() {
+        boolean redo = count != 0;
+        while (redo) {
+            redo = false;
+            redirect = new int[count];
+            order = (Entry<E>[])Array.newInstance(entryComponent, count);
+
+            Bucket<E>[] sorted = createBuckets();
+            int free = 0;
+
+            for (Bucket<E> bucket : sorted) {
+                if (bucket.getSize() != 1) {
+                    if (!collidedEntries(bucket, count)) {
+                        redo = true;
+                        break;
+                    }
+                } else {
+                    for ( ; free < count && order[free] != null; free++) {}
+
+                    if (free >= count) {
+                        redo = true;
+                        break;
+                    }
+
+                    order[free] = bucket.getFirst();
+                    redirect[bucket.hashCode() % count] = -1 - free;
+                    free++;
+                }
+            }
+
+            if (redo) {
+                count = (count + 1) | 1;
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private Bucket<E>[] createBuckets() {
+        Bucket<E>[] buckets = (Bucket<E>[])Array.newInstance(bucketComponent, count);
+
+        map.values().stream().forEach((entry) -> {
+            int index = entry.hashCode() % count;
+            Bucket<E> bucket = buckets[index];
+
+            if (bucket == null) {
+                buckets[index] = bucket = new Bucket<>();
+            }
+
+            bucket.add(entry);
+        });
+
+        Bucket<E>[] sorted = Arrays.asList(buckets).stream()
+                .filter((bucket) -> (bucket != null))
+                .sorted()
+                .toArray((length) -> {
+                    return (Bucket<E>[])Array.newInstance(bucketComponent, length);
+                });
+
+        return sorted;
+    }
+
+    private boolean collidedEntries(Bucket<E> bucket, int count) {
+        List<Integer> undo = new ArrayList<>();
+        int seed = UTF8String.HASH_MULTIPLIER + 1;
+        int retry = 0;
+
+        redo:
+        while (true) {
+            for (Entry<E> entry : bucket.getList()) {
+                int index = entry.hashCode(seed) % count;
+                if (order[index] != null) {
+                    if (++retry > RETRY_LIMIT) {
+                        return false;
+                    }
+
+                    undo.stream().forEach((i) -> {
+                        order[i] = null;
+                    });
+
+                    undo.clear();
+                    seed++;
+
+                    if (seed == 0) {
+                        seed = 1;
+                    }
+
+                    continue redo;
+                }
+
+                order[index] = entry;
+                undo.add(index);
+            }
+
+            redirect[bucket.hashCode() % count] = seed;
+
+            break;
+        }
+
+        return true;
+    }
+ }
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/Resource.java	Wed Jul 05 20:41:30 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package jdk.internal.jimage;
-
-/**
- * Resource is a class or resource file.
- */
-public class Resource {
-    private final String name;
-    private final long size;
-    private final long csize;
-
-    public Resource(String name, long size, long csize) {
-        this.name = name;
-        this.size = size;
-        this.csize = csize;
-    }
-
-    /**
-     * Returns the name of this entry.
-     */
-    public String name() {
-        return name;
-    }
-
-    /**
-     * Returns the number of uncompressed bytes for this entry.
-     */
-    public long size() {
-        return size;
-    }
-
-    /**
-     * Returns the number of compressed bytes for this entry; 0 if
-     * uncompressed.
-     */
-    public long csize() {
-        return csize;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("%s uncompressed size %d compressed size %d", name, size, csize);
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ResourcePool.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage;
+
+import jdk.internal.jimage.decompressor.CompressedResourceHeader;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Pool of resources. This class contain the content of a jimage file in the
+ * matter of Resource.
+ */
+public interface ResourcePool {
+
+    /**
+     * Resources visitor
+     */
+    public interface Visitor {
+
+        /**
+         * Called for each visited Resource.
+         *
+         * @param resource The resource to deal with.
+         * @param order Byte order
+         * @param strings
+         * @return A resource or null if the passed resource is to be removed
+         * from the jimage.
+         * @throws Exception
+         */
+        public Resource visit(Resource resource, ByteOrder order,
+                StringTable strings) throws Exception;
+    }
+
+    /**
+     * A JImage Resource. Fully identified by its path.
+     */
+    public static class Resource {
+
+        private final String path;
+        private final ByteBuffer content;
+
+        private final String module;
+
+        public Resource(String path, ByteBuffer content) {
+            Objects.requireNonNull(path);
+            Objects.requireNonNull(content);
+            this.path = path;
+            this.content = content.asReadOnlyBuffer();
+            String[] split = ImageFileCreator.splitPath(path);
+            module = split[0];
+        }
+
+        public String getPath() {
+            return path;
+        }
+
+        public String getModule() {
+            return module;
+        }
+
+        /**
+         * The resource content.
+         *
+         * @return A read only buffer.
+         */
+        public ByteBuffer getContent() {
+            return content;
+        }
+
+        public int getLength() {
+            return content.limit();
+        }
+
+        public byte[] getByteArray() {
+            content.rewind();
+            byte[] array = new byte[content.remaining()];
+            content.get(array);
+            return array;
+        }
+
+        @Override
+        public String toString() {
+            return getPath();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Resource)) {
+                return false;
+            }
+            Resource res = (Resource) obj;
+            return res.path.equals(path);
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 7;
+            hash = 53 * hash + Objects.hashCode(this.path);
+            return hash;
+        }
+    }
+
+    /**
+     * A resource that has been compressed.
+     */
+    public static final class CompressedResource extends Resource {
+
+        private final long uncompressed_size;
+
+        private CompressedResource(String path, ByteBuffer content,
+                long uncompressed_size) {
+            super(path, content);
+            this.uncompressed_size = uncompressed_size;
+        }
+
+        public long getUncompressedSize() {
+            return uncompressed_size;
+        }
+
+        public static CompressedResource newCompressedResource(Resource original,
+                ByteBuffer compressed,
+                String plugin, String pluginConfig, StringTable strings,
+                ByteOrder order) throws Exception {
+            Objects.requireNonNull(original);
+            Objects.requireNonNull(compressed);
+            Objects.requireNonNull(plugin);
+
+            boolean isTerminal = !(original instanceof CompressedResource);
+            long uncompressed_size = original.getLength();
+            if (original instanceof CompressedResource) {
+                CompressedResource comp = (CompressedResource) original;
+                uncompressed_size = comp.getUncompressedSize();
+            }
+            int nameOffset = strings.addString(plugin);
+            int configOffset = -1;
+            if (pluginConfig != null) {
+                configOffset = strings.addString(plugin);
+            }
+            CompressedResourceHeader rh =
+                    new CompressedResourceHeader(compressed.limit(), original.getLength(),
+                    nameOffset, configOffset, isTerminal);
+            // Merge header with content;
+            byte[] h = rh.getBytes(order);
+            ByteBuffer bb = ByteBuffer.allocate(compressed.limit() + h.length);
+            bb.order(order);
+            bb.put(h);
+            bb.put(compressed);
+            ByteBuffer contentWithHeader = ByteBuffer.wrap(bb.array());
+
+            CompressedResource compressedResource =
+                    new CompressedResource(original.getPath(),
+                    contentWithHeader, uncompressed_size);
+            return compressedResource;
+        }
+    }
+
+    /**
+     * Read only state.
+     *
+     * @return true if readonly false otherwise.
+     */
+    public boolean isReadOnly();
+
+    /**
+     * The byte order
+     *
+     * @return
+     */
+    public ByteOrder getByteOrder();
+
+    /**
+     * Add a resource.
+     *
+     * @param resource The Resource to add.
+     * @throws java.lang.Exception If the pool is read only.
+     */
+    public void addResource(Resource resource) throws Exception;
+
+    /**
+     * Check if a resource is contained in the pool.
+     *
+     * @param res The resource to check.
+     * @return true if res is contained, false otherwise.
+     */
+    public boolean contains(Resource res);
+
+    /**
+     * Get all resources contained in this pool instance.
+     *
+     * @return The collection of resources;
+     */
+    public Collection<Resource> getResources();
+
+    /**
+     * Get the resource for the passed path.
+     *
+     * @param path A resource path
+     * @return A Resource instance or null if the resource is not found
+     */
+    public Resource getResource(String path);
+
+    /**
+     * The Image modules. It is computed based on the resources contained by
+     * this ResourcePool instance.
+     *
+     * @return The Image Modules.
+     */
+    public Map<String, Set<String>> getModulePackages();
+
+    /**
+     * Check if this pool contains some resources.
+     *
+     * @return True if contains some resources.
+     */
+    public boolean isEmpty();
+
+    /**
+     * Visit the resources contained in this ResourcePool.
+     *
+     * @param visitor The visitor
+     * @param output The pool to store resources.
+     * @param strings
+     * @throws Exception
+     */
+    public void visit(Visitor visitor, ResourcePool output, StringTable strings)
+            throws Exception;
+
+    public void addTransformedResource(Resource original, ByteBuffer transformed)
+            throws Exception;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ResourcePoolImpl.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Pool of resources. This class contain the content of a jimage file in the
+ * matter of Resource.
+ */
+public class ResourcePoolImpl implements ResourcePool {
+
+    private final Map<String, Resource> resources = new LinkedHashMap<>();
+
+    private final ByteOrder order;
+    private boolean isReadOnly;
+
+    public ResourcePoolImpl(ByteOrder order) {
+        Objects.requireNonNull(order);
+        this.order = order;
+    }
+
+    /**
+     * Make this Resources instance read-only. No resource can be added.
+     */
+    public void setReadOnly() {
+        isReadOnly = true;
+    }
+
+    /**
+     * Read only state.
+     *
+     * @return true if readonly false otherwise.
+     */
+    @Override
+    public boolean isReadOnly() {
+        return isReadOnly;
+    }
+
+    /**
+     * The byte order
+     *
+     * @return
+     */
+    @Override
+    public ByteOrder getByteOrder() {
+        return order;
+    }
+
+    /**
+     * Add a resource.
+     *
+     * @param resource The Resource to add.
+     * @throws java.lang.Exception If the pool is read only.
+     */
+    @Override
+    public void addResource(Resource resource) throws Exception {
+        if (isReadOnly()) {
+            throw new Exception("pool is readonly");
+        }
+        Objects.requireNonNull(resource);
+        if (resources.get(resource.getPath()) != null) {
+            throw new Exception("Resource" + resource.getPath() +
+                    " already present");
+        }
+        resources.put(resource.getPath(), resource);
+    }
+
+    /**
+     * Check if a resource is contained in the pool.
+     *
+     * @param res The resource to check.
+     * @return true if res is contained, false otherwise.
+     */
+    @Override
+    public boolean contains(Resource res) {
+        Objects.requireNonNull(res);
+        try {
+            getResource(res.getPath());
+            return true;
+        } catch (Exception ex) {
+            return false;
+        }
+    }
+
+    /**
+     * Get all resources contained in this pool instance.
+     *
+     * @return The collection of resources;
+     */
+    @Override
+    public Collection<Resource> getResources() {
+        return Collections.unmodifiableCollection(resources.values());
+    }
+
+/**
+     * Get the resource for the passed path.
+     *
+     * @param path A resource path
+     * @return A Resource instance or null if the resource is not found
+     */
+    @Override
+    public Resource getResource(String path) {
+        Objects.requireNonNull(path);
+        return resources.get(path);
+    }
+
+    /**
+     * The Image modules. It is computed based on the resources contained by
+     * this ResourcePool instance.
+     *
+     * @return The Image Modules.
+     */
+    @Override
+    public Map<String, Set<String>> getModulePackages() {
+        Map<String, Set<String>> moduleToPackage = new LinkedHashMap<>();
+        retrieveModulesPackages(moduleToPackage);
+        return moduleToPackage;
+    }
+
+    /**
+     * Check if this pool contains some resources.
+     *
+     * @return True if contains some resources.
+     */
+    @Override
+    public boolean isEmpty() {
+        return resources.isEmpty();
+    }
+
+    /**
+     * Visit the resources contained in this ResourcePool.
+     *
+     * @param visitor The visitor
+     * @param strings
+     * @throws Exception
+     */
+    @Override
+    public void visit(Visitor visitor, ResourcePool output, StringTable strings)
+            throws Exception {
+        for (Resource resource : getResources()) {
+            Resource res = visitor.visit(resource, order, strings);
+            if (res != null) {
+                output.addResource(res);
+            }
+        }
+    }
+
+    @Override
+    public void addTransformedResource(Resource original, ByteBuffer transformed)
+            throws Exception {
+        if (isReadOnly()) {
+            throw new Exception("Pool is readonly");
+        }
+        Objects.requireNonNull(original);
+        Objects.requireNonNull(transformed);
+        if (resources.get(original.getPath()) != null) {
+            throw new Exception("Resource already present");
+        }
+        Resource res = new Resource(original.getPath(), transformed);
+        addResource(res);
+    }
+
+    private void retrieveModulesPackages(Map<String, Set<String>> moduleToPackage) {
+        for (Resource res : resources.values()) {
+            Set<String> pkgs = moduleToPackage.get(res.getModule());
+            if (pkgs == null) {
+                pkgs = new HashSet<>();
+                moduleToPackage.put(res.getModule(), pkgs);
+            }
+            // Module metadata only contains packages with resource files
+            if (ImageFileCreator.isResourcePackage(res.getPath())) {
+                String[] split = ImageFileCreator.splitPath(res.getPath());
+                String pkg = split[1];
+                if (pkg != null && !pkg.isEmpty()) {
+                    pkgs.add(pkg);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/StringTable.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage;
+
+/**
+* Added strings are stored in the jimage strings table.
+*/
+public interface StringTable {
+    /**
+     * Add a string to the jimage strings table.
+     * @param str The string to add.
+     * @return a String identifier.
+     */
+    public int addString(String str);
+
+    /**
+     * Retrieve a string from the passed id.
+     * @param id The string id.
+     * @return The string referenced by the passed id.
+     */
+    public String getString(int id);
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/UTF8String.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/UTF8String.java	Thu Jul 09 22:46:18 2015 -0700
@@ -29,14 +29,18 @@
 import java.util.Arrays;
 
 public final class UTF8String implements CharSequence {
-
     // Same as StandardCharsets.UTF_8 without loading all of the standard charsets
     static final Charset UTF_8 = Charset.forName("UTF-8");
 
     static final int NOT_FOUND = -1;
     static final int HASH_MULTIPLIER = 0x01000193;
-    static final UTF8String EMPTY_STRING  = new UTF8String("");
-    static final UTF8String CLASS_STRING  = new UTF8String(".class");
+    static final UTF8String EMPTY_STRING = new UTF8String("");
+    static final UTF8String SLASH_STRING = new UTF8String("/");
+    static final UTF8String DOT_STRING = new UTF8String(".");
+
+    // TODO This strings are implementation specific and should be defined elsewhere.
+    static final UTF8String MODULES_STRING = new UTF8String("/modules");
+    static final UTF8String PACKAGES_STRING = new UTF8String("/packages");
 
     final byte[] bytes;
     final int offset;
@@ -160,8 +164,8 @@
         return seed & 0x7FFFFFFF;
     }
 
-    int hashCode(int base) {
-        return hashCode(base, bytes, offset, count);
+    int hashCode(int seed) {
+        return hashCode(seed, bytes, offset, count);
     }
 
     @Override
@@ -186,7 +190,7 @@
         return equals(this, (UTF8String)obj);
     }
 
-    private static boolean equals(UTF8String a, UTF8String b) {
+    public static boolean equals(UTF8String a, UTF8String b) {
         if (a == b) {
             return true;
         }
@@ -211,6 +215,10 @@
         return true;
     }
 
+    public byte[] getBytesCopy() {
+        return Arrays.copyOfRange(bytes, offset, offset + count);
+    }
+
     byte[] getBytes() {
         if (offset != 0 || bytes.length != count) {
             return Arrays.copyOfRange(bytes, offset, offset + count);
@@ -232,33 +240,11 @@
     public char charAt(int index) {
         int ch = byteAt(index);
 
-        return (ch & 0x80) != 0 ? (char)ch : '\0';
+        return (ch & 0x80) == 0 ? (char)ch : '\0';
     }
 
     @Override
     public CharSequence subSequence(int start, int end) {
         return (CharSequence)substring(start, end - start);
     }
-
-    static UTF8String match(UTF8String a, UTF8String b) {
-        int aCount = a.count;
-        int bCount = b.count;
-
-        if (aCount < bCount) {
-            return null;
-        }
-
-        byte[] aBytes = a.bytes;
-        byte[] bBytes = b.bytes;
-        int aOffset = a.offset;
-        int bOffset = b.offset;
-
-        for (int i = 0; i < bCount; i++) {
-            if (aBytes[aOffset + i] != bBytes[bOffset + i]) {
-                return null;
-            }
-        }
-
-        return new UTF8String(aBytes, aOffset + bCount, aCount - bCount);
-    }
 }
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/concurrent/ConcurrentPReader.java	Wed Jul 05 20:41:30 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,149 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.internal.jimage.concurrent;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-
-import jdk.internal.jimage.PReader;
-
-import sun.misc.Unsafe;
-
-/**
- * A PReader implementation that supports concurrent pread operations.
- */
-public class ConcurrentPReader extends PReader {
-
-    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
-    private static final long BA_OFFSET = (long) UNSAFE.arrayBaseOffset(byte[].class);
-
-    /**
-     * A temporary buffer that is cached on a per-thread basis.
-     */
-    private static class TemporaryBuffer {
-        static final ThreadLocal<TemporaryBuffer> CACHED_BUFFER =
-             new ThreadLocal<TemporaryBuffer>() {
-                @Override
-                protected TemporaryBuffer initialValue() { return null; }
-             };
-
-        static final TemporaryBuffer NOT_AVAILABLE = new TemporaryBuffer(0L, 0);
-
-        final long address;
-        final int size;
-
-        TemporaryBuffer(long address, int size) {
-            this.address = address;
-            this.size = size;
-        }
-
-        long address() { return address; }
-        int size() { return size; }
-
-        /**
-         * Returns the {@code TemporaryBuffer} for the current thread. The buffer
-         * is guaranteed to be of at least the given size. Returns {@code null}
-         * if a buffer cannot be cached for this thread.
-         */
-        static TemporaryBuffer get(int len) {
-            TemporaryBuffer buffer = CACHED_BUFFER.get();
-
-            // cached buffer large enough?
-            if (buffer != null && buffer.size() >= len) {
-                return buffer;
-            }
-
-            // if this is an InnocuousThread then don't return anything
-            if (buffer == NOT_AVAILABLE)
-                return null;
-
-            if (buffer != null) {
-                // replace buffer in cache with a larger buffer
-                long originalAddress = buffer.address();
-                long address = UNSAFE.allocateMemory(len);
-                buffer = new TemporaryBuffer(address, len);
-                CACHED_BUFFER.set(buffer);
-                UNSAFE.freeMemory(originalAddress);
-            } else {
-                // first usage.
-                if (Thread.currentThread() instanceof sun.misc.InnocuousThread) {
-                    buffer = NOT_AVAILABLE;
-                } else {
-                    long address = UNSAFE.allocateMemory(len);
-                    buffer = new TemporaryBuffer(address, len);
-                }
-                CACHED_BUFFER.set(buffer);
-            }
-            return buffer;
-        }
-    }
-
-    private final FileDescriptor fd;
-
-    private ConcurrentPReader(FileInputStream fis) throws IOException {
-        super(fis.getChannel());
-        this.fd = fis.getFD();
-    }
-
-    public ConcurrentPReader(String file) throws IOException {
-        this(new FileInputStream(file));
-    }
-
-    @Override
-    public byte[] read(int len, long position) throws IOException {
-        // need a temporary area of memory to read into
-        TemporaryBuffer buffer = TemporaryBuffer.get(len);
-        long address;
-        if (buffer == null) {
-            address = UNSAFE.allocateMemory(len);
-        } else {
-            address = buffer.address();
-        }
-        try {
-            int n = pread(fd, address, len, position);
-            if (n != len)
-                throw new InternalError("short read, not handled yet");
-            byte[] result = new byte[n];
-            UNSAFE.copyMemory(null, address, result, BA_OFFSET, len);
-            return result;
-        } finally {
-            if (buffer == null) {
-                UNSAFE.freeMemory(address);
-            }
-        }
-    }
-
-    private static native int pread(FileDescriptor fd, long address, int len, long pos)
-        throws IOException;
-
-    private static native void initIDs();
-
-    static {
-        System.loadLibrary("java");
-        initIDs();
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/decompressor/CompressedResourceHeader.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage.decompressor;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
+import jdk.internal.jimage.decompressor.ResourceDecompressor.StringsProvider;
+
+/**
+ *
+ * A resource header for compressed resource. This class is handled internally,
+ * you don't have to add header to the resource, headers are added automatically
+ * for compressed resources.
+ */
+public final class CompressedResourceHeader {
+
+    private static final int SIZE = 21;
+    public static final int MAGIC = 0xCAFEFAFA;
+    private final int uncompressedSize;
+    private final int compressedSize;
+    private final int decompressorNameOffset;
+    private final int contentOffset;
+    private final boolean isTerminal;
+
+    public CompressedResourceHeader(int compressedSize,
+            int uncompressedSize, int decompressorNameOffset, int contentOffset,
+            boolean isTerminal) {
+        this.compressedSize = compressedSize;
+        this.uncompressedSize = uncompressedSize;
+        this.decompressorNameOffset = decompressorNameOffset;
+        this.contentOffset = contentOffset;
+        this.isTerminal = isTerminal;
+    }
+
+    public boolean isTerminal() {
+        return isTerminal;
+    }
+
+    public int getDecompressorNameOffset() {
+        return decompressorNameOffset;
+    }
+
+    public int getContentOffset() {
+        return contentOffset;
+    }
+
+    public String getStoredContent(StringsProvider provider) {
+        Objects.nonNull(provider);
+        if(contentOffset == -1) {
+            return null;
+        }
+        return provider.getString(contentOffset);
+    }
+
+    public int getUncompressedSize() {
+        return uncompressedSize;
+    }
+
+    public int getResourceSize() {
+        return compressedSize;
+    }
+
+    public byte[] getBytes(ByteOrder order) {
+        Objects.requireNonNull(order);
+        ByteBuffer buffer = ByteBuffer.allocate(SIZE);
+        buffer.order(order);
+        buffer.putInt(MAGIC);
+        buffer.putInt(compressedSize);
+        buffer.putInt(uncompressedSize);
+        buffer.putInt(decompressorNameOffset);
+        buffer.putInt(contentOffset);
+        buffer.put(isTerminal ? (byte)1 : (byte)0);
+        return buffer.array();
+    }
+
+    public static int getSize() {
+        return SIZE;
+    }
+
+    public static CompressedResourceHeader readFromResource(ByteOrder order,
+            byte[] resource) {
+        Objects.requireNonNull(order);
+        Objects.requireNonNull(resource);
+        if (resource.length < getSize()) {
+            return null;
+        }
+        ByteBuffer buffer = ByteBuffer.wrap(resource, 0, SIZE);
+        buffer.order(order);
+        int magic = buffer.getInt();
+        if(magic != MAGIC) {
+            return null;
+        }
+        int size = buffer.getInt();
+        int uncompressedSize = buffer.getInt();
+        int decompressorNameOffset = buffer.getInt();
+        int contentIndex = buffer.getInt();
+        byte isTerminal = buffer.get();
+        return new CompressedResourceHeader(size, uncompressedSize,
+                decompressorNameOffset, contentIndex, isTerminal == 1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/decompressor/Decompressor.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage.decompressor;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import jdk.internal.jimage.decompressor.ResourceDecompressor.StringsProvider;
+
+/**
+ * Entry point to decompress resources.
+ */
+public final class Decompressor {
+
+    private final Map<Integer, ResourceDecompressor> pluginsCache = new HashMap<>();
+
+    public Decompressor() {
+    }
+
+    /**
+     * Decompress a resource.
+     * @param order Byte order.
+     * @param provider Strings provider
+     * @param content The resource content to uncompress.
+     * @return A fully uncompressed resource.
+     * @throws IOException
+     */
+    public byte[] decompressResource(ByteOrder order, StringsProvider provider,
+            byte[] content) throws IOException {
+        Objects.requireNonNull(order);
+        Objects.requireNonNull(provider);
+        Objects.requireNonNull(content);
+        CompressedResourceHeader header;
+        do {
+            header = CompressedResourceHeader.readFromResource(order, content);
+            if (header != null) {
+                ResourceDecompressor decompressor =
+                        pluginsCache.get(header.getDecompressorNameOffset());
+                if (decompressor == null) {
+                    String pluginName =
+                            provider.getString(header.getDecompressorNameOffset());
+                    if (pluginName == null) {
+                        throw new IOException("Plugin name not found");
+                    }
+                    String storedContent = header.getStoredContent(provider);
+                    Properties props = new Properties();
+                    if (storedContent != null) {
+                        try (ByteArrayInputStream stream =
+                                new ByteArrayInputStream(storedContent.getBytes());) {
+                            props.loadFromXML(stream);
+                        }
+                    }
+                    decompressor = ResourceDecompressorRepository.
+                            newResourceDecompressor(props, pluginName);
+                    if (decompressor == null) {
+                        throw new IOException("Plugin not found: " + pluginName);
+                    }
+
+                    pluginsCache.put(header.getDecompressorNameOffset(), decompressor);
+                }
+                try {
+                    content = decompressor.decompress(provider, content,
+                            CompressedResourceHeader.getSize(), header.getUncompressedSize());
+                } catch (Exception ex) {
+                    throw new IOException(ex);
+                }
+            }
+        } while (header != null);
+        return content;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/decompressor/ResourceDecompressor.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage.decompressor;
+
+/**
+ *
+ * JImage Decompressor.
+ */
+public interface ResourceDecompressor {
+
+    public interface StringsProvider {
+        public String getString(int offset);
+    }
+    /**
+     * Decompressor unique name.
+     * @return The decompressor name.
+     */
+    public String getName();
+
+    /**
+     * Decompress a resource.
+     * @param strings The String provider
+     * @param content The resource content
+     * @param offset Resource content offset
+     * @param originalSize Uncompressed size
+     * @return Uncompressed resource
+     * @throws Exception
+     */
+    public byte[] decompress(StringsProvider strings, byte[] content, int offset,
+            int originalSize) throws Exception;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/decompressor/ResourceDecompressorFactory.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage.decompressor;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ *
+ * JImage Resource Decompressor factory
+ */
+public abstract class ResourceDecompressorFactory {
+    private final String name;
+    private final String description;
+    private final String arguments;
+
+    protected ResourceDecompressorFactory(String name, String description,
+            String arguments) {
+        this.name = name;
+        this.description = description;
+        this.arguments = arguments;
+    }
+
+    /**
+     * The Factory name.
+     * @return The name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * The Factory description.
+     * @return The description.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * The Factory arguments description.
+     * @return The arguments description.
+     */
+    public String getArgumentsDescription() {
+        return arguments;
+    }
+
+    /**
+     * To build a new decompressor.
+     * @param properties Contains configuration.
+     * @return A new decompressor.
+     * @throws IOException
+     */
+    public abstract ResourceDecompressor newDecompressor(Properties properties)
+            throws IOException;
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/decompressor/ResourceDecompressorRepository.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage.decompressor;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ *
+ * JImage Decompressors. All decompressors must be registered in the static
+ * initializer of this class.
+ */
+public final class ResourceDecompressorRepository {
+
+    private ResourceDecompressorRepository() {
+    }
+
+    private static final Map<String, ResourceDecompressorFactory> factories = new HashMap<>();
+
+    static {
+        registerReaderProvider(new ZipDecompressorFactory());
+    }
+
+    /**
+     * Build a new decompressor for the passed name.
+     * @param properties Contains plugin configuration.
+     * @param name The plugin name to build.
+     * @return A decompressor or null if not found
+     * @throws IOException
+     */
+    public static ResourceDecompressor newResourceDecompressor(Properties properties,
+            String name) throws IOException {
+
+        ResourceDecompressorFactory fact = factories.get(name);
+        if (fact != null) {
+            return fact.newDecompressor(properties);
+        }
+        return null;
+    }
+
+    private static void registerReaderProvider(ResourceDecompressorFactory factory) {
+        factories.put(factory.getName(), factory);
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/decompressor/ZipDecompressor.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage.decompressor;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+/**
+ *
+ * ZIP Decompressor
+ */
+final class ZipDecompressor implements ResourceDecompressor {
+
+    @Override
+    public String getName() {
+        return ZipDecompressorFactory.NAME;
+    }
+
+    static byte[] decompress(byte[] bytesIn, int offset) {
+        Inflater inflater = new Inflater();
+        inflater.setInput(bytesIn, offset, bytesIn.length - offset);
+        ByteArrayOutputStream stream = new ByteArrayOutputStream(bytesIn.length - offset);
+        byte[] buffer = new byte[1024];
+
+        while (!inflater.finished()) {
+            int count;
+
+            try {
+                count = inflater.inflate(buffer);
+            } catch (DataFormatException ex) {
+                return null;
+            }
+
+            stream.write(buffer, 0, count);
+        }
+
+        try {
+            stream.close();
+        } catch (IOException ex) {
+            return null;
+        }
+
+        byte[] bytesOut = stream.toByteArray();
+        inflater.end();
+
+        return bytesOut;
+    }
+
+    @Override
+    public byte[] decompress(StringsProvider reader, byte[] content, int offset,
+            int originalSize) throws Exception {
+        byte[] decompressed = decompress(content, offset);
+        return decompressed;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/decompressor/ZipDecompressorFactory.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.jimage.decompressor;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ *
+ * ZIP decompressor factory
+ */
+public final class ZipDecompressorFactory extends ResourceDecompressorFactory {
+    public static final String NAME = "zip";
+    public ZipDecompressorFactory() {
+        super(NAME, "ZIP Decompression", null);
+    }
+
+    @Override
+    public ResourceDecompressor newDecompressor(Properties properties)
+            throws IOException {
+        return new ZipDecompressor();
+    }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java	Thu Jul 09 22:46:18 2015 -0700
@@ -51,7 +51,7 @@
         this.jrtfs = jrtPath.getFileSystem();
         this.path = jrtPath.getResolvedPath();
         // sanity check
-        if (!jrtfs.isDirectory(path))
+        if (!jrtfs.isDirectory(path, true))
             throw new NotDirectoryException(jrtPath.toString());
 
         // absolute path and does not have funky chars in front like /./java.base
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,6 +25,7 @@
 
 package jdk.internal.jrtfs;
 
+import java.nio.file.LinkOption;
 import java.nio.file.attribute.*;
 import java.io.IOException;
 import java.util.LinkedHashMap;
@@ -48,30 +49,32 @@
 
     private final JrtPath path;
     private final boolean isJrtView;
+    private final LinkOption[] options;
 
-    private JrtFileAttributeView(JrtPath path, boolean isJrtView) {
+    private JrtFileAttributeView(JrtPath path, boolean isJrtView, LinkOption... options) {
         this.path = path;
         this.isJrtView = isJrtView;
+        this.options = options;
     }
 
     @SuppressWarnings("unchecked") // Cast to V
-    static <V extends FileAttributeView> V get(JrtPath path, Class<V> type) {
+    static <V extends FileAttributeView> V get(JrtPath path, Class<V> type, LinkOption... options) {
         if (type == null)
             throw new NullPointerException();
         if (type == BasicFileAttributeView.class)
-            return (V)new JrtFileAttributeView(path, false);
+            return (V)new JrtFileAttributeView(path, false, options);
         if (type == JrtFileAttributeView.class)
-            return (V)new JrtFileAttributeView(path, true);
+            return (V)new JrtFileAttributeView(path, true, options);
         return null;
     }
 
-    static JrtFileAttributeView get(JrtPath path, String type) {
+    static JrtFileAttributeView get(JrtPath path, String type, LinkOption... options) {
         if (type == null)
             throw new NullPointerException();
         if (type.equals("basic"))
-            return new JrtFileAttributeView(path, false);
+            return new JrtFileAttributeView(path, false, options);
         if (type.equals("jjrt"))
-            return new JrtFileAttributeView(path, true);
+            return new JrtFileAttributeView(path, true, options);
         return null;
     }
 
@@ -83,7 +86,7 @@
     @Override
     public JrtFileAttributes readAttributes() throws IOException
     {
-        return path.getAttributes();
+        return path.getAttributes(options);
     }
 
     @Override
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java	Thu Jul 09 22:46:18 2015 -0700
@@ -76,12 +76,12 @@
 
     @Override
     public boolean isSymbolicLink() {
-        return false;
+        return node.isLink();
     }
 
     @Override
     public Object fileKey() {
-        return null;
+        return node.resolveLink(true);
     }
 
     ///////// jrt entry attributes ///////////
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java	Thu Jul 09 22:46:18 2015 -0700
@@ -31,9 +31,9 @@
 import java.nio.ByteBuffer;
 import java.nio.channels.*;
 import java.nio.charset.Charset;
-import java.nio.file.AccessMode;
 import java.nio.file.ClosedFileSystemException;
 import java.nio.file.CopyOption;
+import java.nio.file.LinkOption;
 import java.nio.file.FileStore;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystemException;
@@ -45,16 +45,13 @@
 import java.nio.file.Path;
 import java.nio.file.PathMatcher;
 import java.nio.file.ReadOnlyFileSystemException;
-import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
 import java.nio.file.WatchService;
 import java.nio.file.attribute.FileAttribute;
 import java.nio.file.attribute.FileTime;
 import java.nio.file.attribute.UserPrincipalLookupService;
 import java.nio.file.spi.FileSystemProvider;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -63,8 +60,9 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.regex.Pattern;
-import java.util.stream.Collectors;
+import static java.util.stream.Collectors.toList;
 import jdk.internal.jimage.ImageReader;
 import jdk.internal.jimage.ImageReader.Node;
 import jdk.internal.jimage.UTF8String;
@@ -74,6 +72,7 @@
  */
 class JrtFileSystem extends FileSystem {
     private static final Charset UTF_8 = Charset.forName("UTF-8");
+
     private final JrtFileSystemProvider provider;
     // System image readers
     private ImageReader bootImage;
@@ -109,7 +108,8 @@
         this.extImage = openImage(SystemImages.extImagePath);
         this.appImage = openImage(SystemImages.appImagePath);
 
-        rootPath = new JrtPath(this, new byte[]{'/'});
+        byte[] root = new byte[] { '/' };
+        rootPath = new JrtPath(this, root);
         isOpen = true;
     }
 
@@ -149,12 +149,12 @@
         synchronized(this) {
             isOpen = false;
 
-            // close all image readers and null out
+            // close all image reader and null out
             bootImage.close();
+            bootImage = null;
             extImage.close();
+            extImage = null;
             appImage.close();
-            bootImage = null;
-            extImage = null;
             appImage = null;
         }
     }
@@ -289,21 +289,52 @@
         }
     }
 
-    private NodeAndImage findNode(byte[] path) throws IOException {
-        ImageReader image = bootImage;
+    private NodeAndImage lookup(byte[] path) {
         Node node = bootImage.findNode(path);
+        ImageReader image = bootImage;
         if (node == null) {
+            node = extImage.findNode(path);
             image = extImage;
-            node = extImage.findNode(path);
         }
         if (node == null) {
+            node = appImage.findNode(path);
             image = appImage;
-            node = appImage.findNode(path);
         }
-        if (node == null || node.isHidden()) {
-            throw new NoSuchFileException(getString(path));
+        return node != null? new NodeAndImage(node, image) : null;
+    }
+
+    private NodeAndImage lookupSymbolic(byte[] path) {
+        for (int i = 1; i < path.length; i++) {
+            if (path[i] == (byte)'/') {
+                byte[] prefix = Arrays.copyOfRange(path, 0, i);
+                NodeAndImage ni = lookup(prefix);
+                if (ni == null) {
+                    break;
+                }
+
+                if (ni.node.isLink()) {
+                    Node link = ni.node.resolveLink(true);
+                    // resolved symbolic path concatenated to the rest of the path
+                    UTF8String resPath = link.getName().concat(new UTF8String(path, i));
+                    byte[] resPathBytes = resPath.getBytesCopy();
+                    ni = lookup(resPathBytes);
+                    return ni != null? ni : lookupSymbolic(resPathBytes);
+                }
+            }
         }
-        return new NodeAndImage(node, image);
+
+        return null;
+    }
+
+    private NodeAndImage findNode(byte[] path) throws IOException {
+        NodeAndImage ni = lookup(path);
+        if (ni == null) {
+            ni = lookupSymbolic(path);
+            if (ni == null) {
+                throw new NoSuchFileException(getString(path));
+            }
+        }
+        return ni;
     }
 
     private NodeAndImage checkNode(byte[] path) throws IOException {
@@ -321,10 +352,28 @@
         return ni;
     }
 
+    static boolean followLinks(LinkOption... options) {
+        if (options != null) {
+            for (LinkOption lo : options) {
+                if (lo == LinkOption.NOFOLLOW_LINKS) {
+                    return false;
+                } else if (lo == null) {
+                    throw new NullPointerException();
+                } else {
+                    throw new AssertionError("should not reach here");
+                }
+            }
+        }
+        return true;
+    }
+
     // package private helpers
-    JrtFileAttributes getFileAttributes(byte[] path)
+    JrtFileAttributes getFileAttributes(byte[] path, LinkOption... options)
             throws IOException {
         NodeAndImage ni = checkNode(path);
+        if (ni.node.isLink() && followLinks(options)) {
+            return new JrtFileAttributes(ni.node.resolveLink(true));
+        }
         return new JrtFileAttributes(ni.node);
     }
 
@@ -343,11 +392,13 @@
         return true;
     }
 
-    boolean isDirectory(byte[] path)
+    boolean isDirectory(byte[] path, boolean resolveLinks)
             throws IOException {
         ensureOpen();
         NodeAndImage ni = checkNode(path);
-        return ni.node.isDirectory();
+        return resolveLinks && ni.node.isLink()?
+            ni.node.resolveLink(true).isDirectory() :
+            ni.node.isDirectory();
     }
 
     JrtPath toJrtPath(String path) {
@@ -358,6 +409,28 @@
         return new JrtPath(this, path);
     }
 
+    boolean isSameFile(JrtPath p1, JrtPath p2) throws IOException {
+        NodeAndImage n1 = findNode(p1.getName());
+        NodeAndImage n2 = findNode(p2.getName());
+        return n1.node.equals(n2.node);
+    }
+
+    boolean isLink(JrtPath jrtPath) throws IOException {
+        return findNode(jrtPath.getName()).node.isLink();
+    }
+
+    JrtPath resolveLink(JrtPath jrtPath) throws IOException {
+        NodeAndImage ni = findNode(jrtPath.getName());
+        if (ni.node.isLink()) {
+            Node node = ni.node.resolveLink();
+            return toJrtPath(node.getName().getBytesCopy());
+        }
+
+        return jrtPath;
+    }
+
+    private Map<UTF8String, List<Node>> packagesTreeChildren = new ConcurrentHashMap<>();
+
     /**
      * returns the list of child paths of the given directory "path"
      *
@@ -369,49 +442,73 @@
     Iterator<Path> iteratorOf(byte[] path, String childPrefix)
             throws IOException {
         NodeAndImage ni = checkNode(path);
-        if (!ni.node.isDirectory()) {
+        Node node = ni.node.resolveLink(true);
+
+        if (!node.isDirectory()) {
             throw new NotDirectoryException(getString(path));
         }
 
-        if (ni.node.isRootDir()) {
+        if (node.isRootDir()) {
             return rootDirIterator(path, childPrefix);
+        } else if (node.isModulesDir()) {
+            return modulesDirIterator(path, childPrefix);
+        } else if (node.isPackagesDir()) {
+            return packagesDirIterator(path, childPrefix);
+        } else if (node.getNameString().startsWith("/packages/")) {
+            if (ni.image != appImage) {
+                UTF8String name = node.getName();
+                List<Node> children = packagesTreeChildren.get(name);
+                if (children != null) {
+                    return nodesToIterator(toJrtPath(path), childPrefix, children);
+                }
+
+                children = new ArrayList<>();
+                children.addAll(node.getChildren());
+                Node tmpNode = null;
+                // found in boot
+                if (ni.image == bootImage) {
+                    tmpNode = extImage.findNode(name);
+                    if (tmpNode != null) {
+                        children.addAll(tmpNode.getChildren());
+                    }
+                }
+
+                // found in ext
+                tmpNode = appImage.findNode(name);
+                if (tmpNode != null) {
+                    children.addAll(tmpNode.getChildren());
+                }
+
+                packagesTreeChildren.put(name, children);
+                return nodesToIterator(toJrtPath(path), childPrefix, children);
+            }
         }
 
-        return nodesToIterator(toJrtPath(path), childPrefix, ni.node.getChildren());
+        return nodesToIterator(toJrtPath(path), childPrefix, node.getChildren());
     }
 
     private Iterator<Path> nodesToIterator(Path path, String childPrefix, List<Node> childNodes) {
-        List<Path> childPaths;
-        if (childPrefix == null) {
-            childPaths = childNodes.stream()
-                .filter(Node::isVisible)
-                .map(child -> toJrtPath(child.getNameString()))
-                .collect(Collectors.toCollection(ArrayList::new));
-        } else {
-            childPaths = childNodes.stream()
-                .filter(Node::isVisible)
-                .map(child -> toJrtPath(childPrefix + child.getNameString().substring(1)))
-                .collect(Collectors.toCollection(ArrayList::new));
-        }
-        return childPaths.iterator();
+        Function<Node, Path> f = childPrefix == null
+                ? child -> toJrtPath(child.getNameString())
+                : child -> toJrtPath(childPrefix + child.getNameString().substring(1));
+         return childNodes.stream().map(f).collect(toList()).iterator();
     }
 
-    private List<Node> rootChildren;
-    private static void addRootDirContent(List<Node> dest, List<Node> src) {
-        for (Node n : src) {
-            // only module directories at the top level. Filter other stuff!
-            if (n.isModuleDir()) {
-                dest.add(n);
+    private void addRootDirContent(List<Node> children) {
+        for (Node child : children) {
+            if (!(child.isModulesDir() || child.isPackagesDir())) {
+                rootChildren.add(child);
             }
         }
     }
 
+    private List<Node> rootChildren;
     private synchronized void initRootChildren(byte[] path) {
         if (rootChildren == null) {
             rootChildren = new ArrayList<>();
-            addRootDirContent(rootChildren, bootImage.findNode(path).getChildren());
-            addRootDirContent(rootChildren, extImage.findNode(path).getChildren());
-            addRootDirContent(rootChildren, appImage.findNode(path).getChildren());
+            rootChildren.addAll(bootImage.findNode(path).getChildren());
+            addRootDirContent(extImage.findNode(path).getChildren());
+            addRootDirContent(appImage.findNode(path).getChildren());
         }
     }
 
@@ -420,6 +517,35 @@
         return nodesToIterator(rootPath, childPrefix, rootChildren);
     }
 
+    private List<Node> modulesChildren;
+    private synchronized void initModulesChildren(byte[] path) {
+        if (modulesChildren == null) {
+            modulesChildren = new ArrayList<>();
+            modulesChildren.addAll(bootImage.findNode(path).getChildren());
+            modulesChildren.addAll(appImage.findNode(path).getChildren());
+            modulesChildren.addAll(extImage.findNode(path).getChildren());
+        }
+    }
+
+    private Iterator<Path> modulesDirIterator(byte[] path, String childPrefix) throws IOException {
+        initModulesChildren(path);
+        return nodesToIterator(new JrtPath(this, path), childPrefix, modulesChildren);
+    }
+
+    private List<Node> packagesChildren;
+    private synchronized void initPackagesChildren(byte[] path) {
+        if (packagesChildren == null) {
+            packagesChildren = new ArrayList<>();
+            packagesChildren.addAll(bootImage.findNode(path).getChildren());
+            packagesChildren.addAll(extImage.findNode(path).getChildren());
+            packagesChildren.addAll(appImage.findNode(path).getChildren());
+        }
+    }
+    private Iterator<Path> packagesDirIterator(byte[] path, String childPrefix) throws IOException {
+        initPackagesChildren(path);
+        return nodesToIterator(new JrtPath(this, path), childPrefix, packagesChildren);
+    }
+
     void createDirectory(byte[] dir, FileAttribute<?>... attrs)
             throws IOException {
         throw readOnly();
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java	Thu Jul 09 22:46:18 2015 -0700
@@ -146,6 +146,11 @@
     }
 
     @Override
+    public Path readSymbolicLink(Path link) throws IOException {
+        return toJrtPath(link).readSymbolicLink();
+    }
+
+    @Override
     public void copy(Path src, Path target, CopyOption... options)
         throws IOException
     {
@@ -169,7 +174,7 @@
     public <V extends FileAttributeView> V
         getFileAttributeView(Path path, Class<V> type, LinkOption... options)
     {
-        return JrtFileAttributeView.get(toJrtPath(path), type);
+        return JrtFileAttributeView.get(toJrtPath(path), type, options);
     }
 
     @Override
@@ -250,7 +255,7 @@
         throws IOException
     {
         if (type == BasicFileAttributes.class || type == JrtFileAttributes.class)
-            return (A)toJrtPath(path).getAttributes();
+            return (A)toJrtPath(path).getAttributes(options);
         return null;
     }
 
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java	Thu Jul 09 22:46:18 2015 -0700
@@ -55,6 +55,10 @@
             this.path = normalize(path);
     }
 
+    byte[] getName() {
+        return path;
+    }
+
     @Override
     public JrtPath getRoot() {
         if (this.isAbsolute())
@@ -140,10 +144,19 @@
     @Override
     public JrtPath toRealPath(LinkOption... options) throws IOException {
         JrtPath realPath = new JrtPath(jrtfs, getResolvedPath()).toAbsolutePath();
+        realPath = JrtFileSystem.followLinks(options)? jrtfs.resolveLink(this) : realPath;
         realPath.checkAccess();
         return realPath;
     }
 
+    JrtPath readSymbolicLink() throws IOException {
+        if (! jrtfs.isLink(this)) {
+           throw new IOException("not a symbolic link");
+        }
+
+        return jrtfs.resolveLink(this);
+    }
+
     boolean isHidden() {
         return false;
     }
@@ -638,9 +651,9 @@
         jrtfs.deleteFile(getResolvedPath(), false);
     }
 
-    JrtFileAttributes getAttributes() throws IOException
+    JrtFileAttributes getAttributes(LinkOption... options) throws IOException
     {
-        JrtFileAttributes zfas = jrtfs.getFileAttributes(getResolvedPath());
+        JrtFileAttributes zfas = jrtfs.getFileAttributes(getResolvedPath(), options);
         if (zfas == null)
             throw new NoSuchFileException(toString());
         return zfas;
@@ -659,7 +672,7 @@
             type = attribute.substring(0, colonPos++);
             attr = attribute.substring(colonPos);
         }
-        JrtFileAttributeView view = JrtFileAttributeView.get(this, type);
+        JrtFileAttributeView view = JrtFileAttributeView.get(this, type, options);
         if (view == null)
             throw new UnsupportedOperationException("view <" + view + "> is not supported");
         view.setAttribute(attr, value);
@@ -685,7 +698,7 @@
             view = attributes.substring(0, colonPos++);
             attrs = attributes.substring(colonPos);
         }
-        JrtFileAttributeView jrtfv = JrtFileAttributeView.get(this, view);
+        JrtFileAttributeView jrtfv = JrtFileAttributeView.get(this, view, options);
         if (jrtfv == null) {
             throw new UnsupportedOperationException("view not supported");
         }
@@ -706,9 +719,10 @@
             this.getFileSystem() != other.getFileSystem())
             return false;
         this.checkAccess();
-        ((JrtPath)other).checkAccess();
-        return Arrays.equals(this.getResolvedPath(),
-                             ((JrtPath)other).getResolvedPath());
+        JrtPath path = (JrtPath)other;
+        path.checkAccess();
+        return Arrays.equals(this.getResolvedPath(), path.getResolvedPath()) ||
+            jrtfs.isSameFile(this, (JrtPath)other);
     }
 
     SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
--- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/SystemImages.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/SystemImages.java	Thu Jul 09 22:46:18 2015 -0700
@@ -42,6 +42,7 @@
     static final Path bootImagePath;
     static final Path extImagePath;
     static final Path appImagePath;
+
     static {
         PrivilegedAction<String> pa = SystemImages::findHome;
         RUNTIME_HOME = AccessController.doPrivileged(pa);
--- a/jdk/src/java.base/share/classes/sun/misc/Unsafe.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/misc/Unsafe.java	Thu Jul 09 22:46:18 2015 -0700
@@ -31,6 +31,8 @@
 import sun.reflect.CallerSensitive;
 import sun.reflect.Reflection;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
+
 
 /**
  * A collection of methods for performing low-level, unsafe operations.
@@ -148,6 +150,7 @@
      * @throws RuntimeException No defined exceptions are thrown, not even
      *         {@link NullPointerException}
      */
+    @HotSpotIntrinsicCandidate
     public native int getInt(Object o, long offset);
 
     /**
@@ -170,12 +173,14 @@
      * @throws RuntimeException No defined exceptions are thrown, not even
      *         {@link NullPointerException}
      */
+    @HotSpotIntrinsicCandidate
     public native void putInt(Object o, long offset, int x);
 
     /**
      * Fetches a reference value from a given Java variable.
      * @see #getInt(Object, long)
      */
+    @HotSpotIntrinsicCandidate
     public native Object getObject(Object o, long offset);
 
     /**
@@ -188,35 +193,50 @@
      * are updated.
      * @see #putInt(Object, long, int)
      */
+    @HotSpotIntrinsicCandidate
     public native void putObject(Object o, long offset, Object x);
 
     /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
     public native boolean getBoolean(Object o, long offset);
     /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
     public native void    putBoolean(Object o, long offset, boolean x);
     /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
     public native byte    getByte(Object o, long offset);
     /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
     public native void    putByte(Object o, long offset, byte x);
     /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
     public native short   getShort(Object o, long offset);
     /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
     public native void    putShort(Object o, long offset, short x);
     /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
     public native char    getChar(Object o, long offset);
     /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
     public native void    putChar(Object o, long offset, char x);
     /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
     public native long    getLong(Object o, long offset);
     /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
     public native void    putLong(Object o, long offset, long x);
     /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
     public native float   getFloat(Object o, long offset);
     /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
     public native void    putFloat(Object o, long offset, float x);
     /** @see #getInt(Object, long) */
+    @HotSpotIntrinsicCandidate
     public native double  getDouble(Object o, long offset);
     /** @see #putInt(Object, long, int) */
+    @HotSpotIntrinsicCandidate
     public native void    putDouble(Object o, long offset, double x);
 
     // These read VM internal data.
@@ -257,6 +277,7 @@
      *
      * @see #allocateMemory
      */
+    @HotSpotIntrinsicCandidate
     public native byte    getByte(long address);
 
     /**
@@ -266,31 +287,44 @@
      *
      * @see #getByte(long)
      */
+    @HotSpotIntrinsicCandidate
     public native void    putByte(long address, byte x);
 
     /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
     public native short   getShort(long address);
     /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
     public native void    putShort(long address, short x);
     /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
     public native char    getChar(long address);
     /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
     public native void    putChar(long address, char x);
     /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
     public native int     getInt(long address);
     /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
     public native void    putInt(long address, int x);
     /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
     public native long    getLong(long address);
     /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
     public native void    putLong(long address, long x);
     /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
     public native float   getFloat(long address);
     /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
     public native void    putFloat(long address, float x);
     /** @see #getByte(long) */
+    @HotSpotIntrinsicCandidate
     public native double  getDouble(long address);
     /** @see #putByte(long, byte) */
+    @HotSpotIntrinsicCandidate
     public native void    putDouble(long address, double x);
 
     /**
@@ -307,6 +341,7 @@
      *
      * @see #allocateMemory
      */
+    @HotSpotIntrinsicCandidate
     public native long getAddress(long address);
 
     /**
@@ -319,6 +354,7 @@
      *
      * @see #getAddress(long)
      */
+    @HotSpotIntrinsicCandidate
     public native void putAddress(long address, long x);
 
     /// wrappers for malloc, realloc, free:
@@ -406,6 +442,7 @@
      *
      * @since 1.7
      */
+    @HotSpotIntrinsicCandidate
     public native void copyMemory(Object srcBase, long srcOffset,
                                   Object destBase, long destOffset,
                                   long bytes);
@@ -651,6 +688,7 @@
      * Allocates an instance but does not run any constructor.
      * Initializes the class if it has not yet been.
      */
+    @HotSpotIntrinsicCandidate
     public native Object allocateInstance(Class<?> cls)
         throws InstantiationException;
 
@@ -666,6 +704,7 @@
      *
      * @return {@code true} if successful
      */
+    @HotSpotIntrinsicCandidate
     public final native boolean compareAndSwapObject(Object o, long offset,
                                                      Object expected,
                                                      Object x);
@@ -679,6 +718,7 @@
      *
      * @return {@code true} if successful
      */
+    @HotSpotIntrinsicCandidate
     public final native boolean compareAndSwapInt(Object o, long offset,
                                                   int expected,
                                                   int x);
@@ -692,6 +732,7 @@
      *
      * @return {@code true} if successful
      */
+    @HotSpotIntrinsicCandidate
     public final native boolean compareAndSwapLong(Object o, long offset,
                                                    long expected,
                                                    long x);
@@ -700,60 +741,78 @@
      * Fetches a reference value from a given Java variable, with volatile
      * load semantics. Otherwise identical to {@link #getObject(Object, long)}
      */
+    @HotSpotIntrinsicCandidate
     public native Object getObjectVolatile(Object o, long offset);
 
     /**
      * Stores a reference value into a given Java variable, with
      * volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)}
      */
+    @HotSpotIntrinsicCandidate
     public native void    putObjectVolatile(Object o, long offset, Object x);
 
     /** Volatile version of {@link #getInt(Object, long)}  */
+    @HotSpotIntrinsicCandidate
     public native int     getIntVolatile(Object o, long offset);
 
     /** Volatile version of {@link #putInt(Object, long, int)}  */
+    @HotSpotIntrinsicCandidate
     public native void    putIntVolatile(Object o, long offset, int x);
 
     /** Volatile version of {@link #getBoolean(Object, long)}  */
+    @HotSpotIntrinsicCandidate
     public native boolean getBooleanVolatile(Object o, long offset);
 
     /** Volatile version of {@link #putBoolean(Object, long, boolean)}  */
+    @HotSpotIntrinsicCandidate
     public native void    putBooleanVolatile(Object o, long offset, boolean x);
 
     /** Volatile version of {@link #getByte(Object, long)}  */
+    @HotSpotIntrinsicCandidate
     public native byte    getByteVolatile(Object o, long offset);
 
     /** Volatile version of {@link #putByte(Object, long, byte)}  */
+    @HotSpotIntrinsicCandidate
     public native void    putByteVolatile(Object o, long offset, byte x);
 
     /** Volatile version of {@link #getShort(Object, long)}  */
+    @HotSpotIntrinsicCandidate
     public native short   getShortVolatile(Object o, long offset);
 
     /** Volatile version of {@link #putShort(Object, long, short)}  */
+    @HotSpotIntrinsicCandidate
     public native void    putShortVolatile(Object o, long offset, short x);
 
     /** Volatile version of {@link #getChar(Object, long)}  */
+    @HotSpotIntrinsicCandidate
     public native char    getCharVolatile(Object o, long offset);
 
     /** Volatile version of {@link #putChar(Object, long, char)}  */
+    @HotSpotIntrinsicCandidate
     public native void    putCharVolatile(Object o, long offset, char x);
 
     /** Volatile version of {@link #getLong(Object, long)}  */
+    @HotSpotIntrinsicCandidate
     public native long    getLongVolatile(Object o, long offset);
 
     /** Volatile version of {@link #putLong(Object, long, long)}  */
+    @HotSpotIntrinsicCandidate
     public native void    putLongVolatile(Object o, long offset, long x);
 
     /** Volatile version of {@link #getFloat(Object, long)}  */
+    @HotSpotIntrinsicCandidate
     public native float   getFloatVolatile(Object o, long offset);
 
     /** Volatile version of {@link #putFloat(Object, long, float)}  */
+    @HotSpotIntrinsicCandidate
     public native void    putFloatVolatile(Object o, long offset, float x);
 
     /** Volatile version of {@link #getDouble(Object, long)}  */
+    @HotSpotIntrinsicCandidate
     public native double  getDoubleVolatile(Object o, long offset);
 
     /** Volatile version of {@link #putDouble(Object, long, double)}  */
+    @HotSpotIntrinsicCandidate
     public native void    putDoubleVolatile(Object o, long offset, double x);
 
     /**
@@ -765,12 +824,15 @@
      *
      * Corresponds to C11 atomic_store_explicit(..., memory_order_release).
      */
+    @HotSpotIntrinsicCandidate
     public native void    putOrderedObject(Object o, long offset, Object x);
 
     /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)}  */
+    @HotSpotIntrinsicCandidate
     public native void    putOrderedInt(Object o, long offset, int x);
 
     /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */
+    @HotSpotIntrinsicCandidate
     public native void    putOrderedLong(Object o, long offset, long x);
 
     /**
@@ -785,6 +847,7 @@
      *
      * @param thread the thread to unpark.
      */
+    @HotSpotIntrinsicCandidate
     public native void unpark(Object thread);
 
     /**
@@ -798,6 +861,7 @@
      * because {@code unpark} is, so it would be strange to place it
      * elsewhere.
      */
+    @HotSpotIntrinsicCandidate
     public native void park(boolean isAbsolute, long time);
 
     /**
@@ -831,6 +895,7 @@
      * @return the previous value
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public final int getAndAddInt(Object o, long offset, int delta) {
         int v;
         do {
@@ -850,6 +915,7 @@
      * @return the previous value
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public final long getAndAddLong(Object o, long offset, long delta) {
         long v;
         do {
@@ -869,6 +935,7 @@
      * @return the previous value
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public final int getAndSetInt(Object o, long offset, int newValue) {
         int v;
         do {
@@ -888,6 +955,7 @@
      * @return the previous value
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public final long getAndSetLong(Object o, long offset, long newValue) {
         long v;
         do {
@@ -907,6 +975,7 @@
      * @return the previous value
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public final Object getAndSetObject(Object o, long offset, Object newValue) {
         Object v;
         do {
@@ -928,6 +997,7 @@
      * provide a LoadLoad barrier also provide a LoadStore barrier for free.
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public native void loadFence();
 
     /**
@@ -942,6 +1012,7 @@
      * provide a StoreStore barrier also provide a LoadStore barrier for free.
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public native void storeFence();
 
     /**
@@ -953,6 +1024,7 @@
      * Corresponds to C11 atomic_thread_fence(memory_order_seq_cst).
      * @since 1.8
      */
+    @HotSpotIntrinsicCandidate
     public native void fullFence();
 
     /**
@@ -1010,6 +1082,7 @@
      *         {@link NullPointerException}
      * @since 1.9
      */
+    @HotSpotIntrinsicCandidate
     public final long getLongUnaligned(Object o, long offset) {
         if ((offset & 7) == 0) {
             return getLong(o, offset);
@@ -1048,6 +1121,7 @@
     }
 
     /** @see #getLongUnaligned(Object, long) */
+    @HotSpotIntrinsicCandidate
     public final int getIntUnaligned(Object o, long offset) {
         if ((offset & 3) == 0) {
             return getInt(o, offset);
@@ -1067,6 +1141,7 @@
     }
 
     /** @see #getLongUnaligned(Object, long) */
+    @HotSpotIntrinsicCandidate
     public final short getShortUnaligned(Object o, long offset) {
         if ((offset & 1) == 0) {
             return getShort(o, offset);
@@ -1081,9 +1156,11 @@
     }
 
     /** @see #getLongUnaligned(Object, long) */
+    @HotSpotIntrinsicCandidate
     public final char getCharUnaligned(Object o, long offset) {
         return (char)getShortUnaligned(o, offset);
     }
+
     /** @see #getLongUnaligned(Object, long, boolean) */
     public final char getCharUnaligned(Object o, long offset, boolean bigEndian) {
         return convEndian(bigEndian, getCharUnaligned(o, offset));
@@ -1117,6 +1194,7 @@
      *         {@link NullPointerException}
      * @since 1.9
      */
+    @HotSpotIntrinsicCandidate
     public final void putLongUnaligned(Object o, long offset, long x) {
         if ((offset & 7) == 0) {
             putLong(o, offset, x);
@@ -1142,6 +1220,7 @@
                          (byte)(x >>> 56));
         }
     }
+
     /**
      * As {@link #putLongUnaligned(Object, long, long)} but with an additional
      * argument which specifies the endianness of the value as stored in memory.
@@ -1158,6 +1237,7 @@
     }
 
     /** @see #putLongUnaligned(Object, long, long) */
+    @HotSpotIntrinsicCandidate
     public final void putIntUnaligned(Object o, long offset, int x) {
         if ((offset & 3) == 0) {
             putInt(o, offset, x);
@@ -1179,6 +1259,7 @@
     }
 
     /** @see #putLongUnaligned(Object, long, long) */
+    @HotSpotIntrinsicCandidate
     public final void putShortUnaligned(Object o, long offset, short x) {
         if ((offset & 1) == 0) {
             putShort(o, offset, x);
@@ -1194,6 +1275,7 @@
     }
 
     /** @see #putLongUnaligned(Object, long, long) */
+    @HotSpotIntrinsicCandidate
     public final void putCharUnaligned(Object o, long offset, char x) {
         putShortUnaligned(o, offset, (short)x);
     }
--- a/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java	Thu Jul 09 22:46:18 2015 -0700
@@ -32,6 +32,9 @@
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.CoderResult;
 import java.util.Arrays;
+import java.util.Objects;
+
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 class ISO_8859_1
     extends Charset
@@ -147,9 +150,16 @@
 
         private final Surrogate.Parser sgp = new Surrogate.Parser();
 
-        // JVM may replace this method with intrinsic code.
+        // Method possible replaced with a compiler intrinsic.
         private static int encodeISOArray(char[] sa, int sp,
-                                          byte[] da, int dp, int len)
+                                          byte[] da, int dp, int len) {
+            encodeISOArrayCheck(sa, sp, da, dp, len);
+            return implEncodeISOArray(sa, sp, da, dp, len);
+        }
+
+        @HotSpotIntrinsicCandidate
+        private static int implEncodeISOArray(char[] sa, int sp,
+                                              byte[] da, int dp, int len)
         {
             int i = 0;
             for (; i < len; i++) {
@@ -161,6 +171,34 @@
             return i;
         }
 
+        private static void encodeISOArrayCheck(char[] sa, int sp,
+                                                byte[] da, int dp, int len) {
+            if (len <= 0) {
+                return;  // not an error because encodeISOArrayImpl won't execute if len <= 0
+            }
+
+            Objects.requireNonNull(sa);
+            Objects.requireNonNull(da);
+
+            if (sp < 0 || sp >= sa.length) {
+                throw new ArrayIndexOutOfBoundsException(sp);
+            }
+
+            if (dp < 0 || dp >= da.length) {
+                throw new ArrayIndexOutOfBoundsException(dp);
+            }
+
+            int endIndexSP = sp + len - 1;
+            if (endIndexSP < 0 || endIndexSP >= sa.length) {
+                throw new ArrayIndexOutOfBoundsException(endIndexSP);
+            }
+
+            int endIndexDP = dp + len - 1;
+            if (endIndexDP < 0 || endIndexDP >= da.length) {
+                throw new ArrayIndexOutOfBoundsException(endIndexDP);
+            }
+        }
+
         private CoderResult encodeArrayLoop(CharBuffer src,
                                             ByteBuffer dst)
         {
--- a/jdk/src/java.base/share/classes/sun/reflect/Reflection.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/reflect/Reflection.java	Thu Jul 09 22:46:18 2015 -0700
@@ -28,6 +28,7 @@
 import java.lang.reflect.*;
 import java.util.HashMap;
 import java.util.Map;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /** Common utility routines used by both java.lang and
     java.lang.reflect */
@@ -56,6 +57,7 @@
         ignoring frames associated with java.lang.reflect.Method.invoke()
         and its implementation. */
     @CallerSensitive
+    @HotSpotIntrinsicCandidate
     public static native Class<?> getCallerClass();
 
     /**
@@ -74,6 +76,7 @@
         to compatibility reasons; see 4471811. Only the values of the
         low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
         valid. */
+    @HotSpotIntrinsicCandidate
     public static native int getClassAccessFlags(Class<?> c);
 
     /** A quick "fast-path" check to try to avoid getCallerClass()
--- a/jdk/src/java.base/share/classes/sun/security/provider/DigestBase.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/security/provider/DigestBase.java	Thu Jul 09 22:46:18 2015 -0700
@@ -28,6 +28,9 @@
 import java.security.MessageDigestSpi;
 import java.security.DigestException;
 import java.security.ProviderException;
+import java.util.Objects;
+
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * Common base message digest implementation for the Sun provider.
@@ -136,12 +139,36 @@
 
     // compress complete blocks
     private int implCompressMultiBlock(byte[] b, int ofs, int limit) {
+        implCompressMultiBlockCheck(b, ofs, limit);
+        return implCompressMultiBlock0(b, ofs, limit);
+    }
+
+    @HotSpotIntrinsicCandidate
+    private int implCompressMultiBlock0(byte[] b, int ofs, int limit) {
         for (; ofs <= limit; ofs += blockSize) {
             implCompress(b, ofs);
         }
         return ofs;
     }
 
+    private void implCompressMultiBlockCheck(byte[] b, int ofs, int limit) {
+        if (limit < 0) {
+            return;  // not an error because implCompressMultiBlockImpl won't execute if limit < 0
+                     // and an exception is thrown if ofs < 0.
+        }
+
+        Objects.requireNonNull(b);
+
+        if (ofs < 0 || ofs >= b.length) {
+            throw new ArrayIndexOutOfBoundsException(ofs);
+        }
+
+        int endIndex = (limit / blockSize) * blockSize  + blockSize - 1;
+        if (endIndex >= b.length) {
+            throw new ArrayIndexOutOfBoundsException(endIndex);
+        }
+    }
+
     // reset this object. See JCA doc.
     protected final void engineReset() {
         if (bytesProcessed == 0) {
--- a/jdk/src/java.base/share/classes/sun/security/provider/SHA.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/security/provider/SHA.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,7 +25,10 @@
 
 package sun.security.provider;
 
+import java.util.Objects;
+
 import static sun.security.provider.ByteArrayAccess.*;
+import jdk.internal.HotSpotIntrinsicCandidate;
 
 /**
  * This class implements the Secure Hash Algorithm (SHA) developed by
@@ -114,8 +117,27 @@
      * "old" NIST Secure Hash Algorithm.
      */
     void implCompress(byte[] buf, int ofs) {
+        implCompressCheck(buf, ofs);
+        implCompress0(buf, ofs);
+    }
+
+    private void implCompressCheck(byte[] buf, int ofs) {
+        Objects.requireNonNull(buf);
+
+        // The checks performed by the method 'b2iBig64'
+        // are sufficient for the case when the method
+        // 'implCompressImpl' is replaced with a compiler
+        // intrinsic.
         b2iBig64(buf, ofs, W);
+    }
 
+    // The method 'implCompressImpl seems not to use its parameters.
+    // The method can, however, be replaced with a compiler intrinsic
+    // that operates directly on the array 'buf' (starting from
+    // offset 'ofs') and not on array 'W', therefore 'buf' and 'ofs'
+    // must be passed as parameter to the method.
+    @HotSpotIntrinsicCandidate
+    private void implCompress0(byte[] buf, int ofs) {
         // The first 16 ints have the byte stream, compute the rest of
         // the buffer
         for (int t = 16; t <= 79; t++) {
--- a/jdk/src/java.base/share/classes/sun/security/provider/SHA2.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/security/provider/SHA2.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,6 +25,9 @@
 
 package sun.security.provider;
 
+import java.util.Objects;
+
+import jdk.internal.HotSpotIntrinsicCandidate;
 import static sun.security.provider.ByteArrayAccess.*;
 
 /**
@@ -186,8 +189,27 @@
      * Process the current block to update the state variable state.
      */
     void implCompress(byte[] buf, int ofs) {
+        implCompressCheck(buf, ofs);
+        implCompress0(buf, ofs);
+    }
+
+    private void implCompressCheck(byte[] buf, int ofs) {
+        Objects.requireNonNull(buf);
+
+        // The checks performed by the method 'b2iBig64'
+        // are sufficient for the case when the method
+        // 'implCompressImpl' is replaced with a compiler
+        // intrinsic.
         b2iBig64(buf, ofs, W);
+    }
 
+    // The method 'implCompressImpl' seems not to use its parameters.
+    // The method can, however, be replaced with a compiler intrinsic
+    // that operates directly on the array 'buf' (starting from
+    // offset 'ofs') and not on array 'W', therefore 'buf' and 'ofs'
+    // must be passed as parameter to the method.
+    @HotSpotIntrinsicCandidate
+    private void implCompress0(byte[] buf, int ofs) {
         // The first 16 ints are from the byte stream, compute the rest of
         // the W[]'s
         for (int t = 16; t < ITERATION; t++) {
--- a/jdk/src/java.base/share/classes/sun/security/provider/SHA5.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/security/provider/SHA5.java	Thu Jul 09 22:46:18 2015 -0700
@@ -26,8 +26,10 @@
 package sun.security.provider;
 
 import java.security.*;
+import java.util.Objects;
 import java.math.BigInteger;
 
+import jdk.internal.HotSpotIntrinsicCandidate;
 import static sun.security.provider.ByteArrayAccess.*;
 
 /**
@@ -205,8 +207,27 @@
      * "old" NIST Secure Hash Algorithm.
      */
     final void implCompress(byte[] buf, int ofs) {
+        implCompressCheck(buf, ofs);
+        implCompress0(buf, ofs);
+    }
+
+    private void implCompressCheck(byte[] buf, int ofs) {
+        Objects.requireNonNull(buf);
+
+        // The checks performed by the method 'b2iBig128'
+        // are sufficient for the case when the method
+        // 'implCompressImpl' is replaced with a compiler
+        // intrinsic.
         b2lBig128(buf, ofs, W);
+    }
 
+    // The method 'implCompressImpl' seems not to use its parameters.
+    // The method can, however, be replaced with a compiler intrinsic
+    // that operates directly on the array 'buf' (starting from
+    // offset 'ofs') and not on array 'W', therefore 'buf' and 'ofs'
+    // must be passed as parameter to the method.
+    @HotSpotIntrinsicCandidate
+    private final void implCompress0(byte[] buf, int ofs) {
         // The first 16 longs are from the byte stream, compute the rest of
         // the W[]'s
         for (int t = 16; t < ITERATION; t++) {
--- a/jdk/src/java.base/share/native/include/jvm.h	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/native/include/jvm.h	Thu Jul 09 22:46:18 2015 -0700
@@ -557,6 +557,48 @@
 JVM_SupportsCX8(void);
 
 /*
+ * jdk.internal.jimage
+ */
+
+JNIEXPORT jlong JNICALL
+JVM_ImageOpen(JNIEnv *env, const char *nativePath, jboolean big_endian);
+
+JNIEXPORT void JNICALL
+JVM_ImageClose(JNIEnv *env, jlong id);
+
+JNIEXPORT jlong JNICALL
+JVM_ImageGetIndexAddress(JNIEnv *env, jlong id);
+
+JNIEXPORT jlong JNICALL
+JVM_ImageGetDataAddress(JNIEnv *env,jlong id);
+
+JNIEXPORT jboolean JNICALL
+JVM_ImageRead(JNIEnv *env, jlong id, jlong offset,
+            unsigned char* uncompressedAddress, jlong uncompressed_size);
+
+JNIEXPORT jboolean JNICALL
+JVM_ImageReadCompressed(JNIEnv *env, jlong id, jlong offset,
+            unsigned char* compressedBuffer, jlong compressed_size,
+            unsigned char* uncompressedBuffer, jlong uncompressed_size);
+
+JNIEXPORT const char* JNICALL
+JVM_ImageGetStringBytes(JNIEnv *env, jlong id, jint offset);
+
+JNIEXPORT jlong* JNICALL
+JVM_ImageGetAttributes(JNIEnv *env, jlong* rawAttributes, jlong id, jint offset);
+
+JNIEXPORT jsize JNICALL
+JVM_ImageGetAttributesCount(JNIEnv *env);
+
+JNIEXPORT jlong* JNICALL
+JVM_ImageFindAttributes(JNIEnv *env, jlong* rawAttributes, jbyte* rawBytes, jsize size, jlong id);
+
+JNIEXPORT jint* JNICALL
+JVM_ImageAttributeOffsets(JNIEnv *env, jint* rawOffsets, unsigned int length, jlong id);
+
+JNIEXPORT unsigned int JNICALL
+JVM_ImageAttributeOffsetsLength(JNIEnv *env, jlong id);
+/*
  * com.sun.dtrace.jsdt support
  */
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/native/libjava/Image.c	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <string.h>
+
+#include "jni.h"
+#include "jvm.h"
+#include "jdk_internal_jimage_ImageNativeSubstrate.h"
+
+JNIEXPORT jlong JNICALL
+Java_jdk_internal_jimage_ImageNativeSubstrate_openImage(JNIEnv *env,
+        jclass cls, jstring path, jboolean big_endian) {
+    const char *nativePath;
+    jlong ret;
+
+    nativePath = (*env)->GetStringUTFChars(env, path, NULL);
+    ret = JVM_ImageOpen(env, nativePath, big_endian);
+    (*env)->ReleaseStringUTFChars(env, path, nativePath);
+    return ret;
+}
+
+JNIEXPORT void JNICALL
+Java_jdk_internal_jimage_ImageNativeSubstrate_closeImage(JNIEnv *env,
+                                        jclass cls, jlong id) {
+    JVM_ImageClose(env, id);
+}
+
+JNIEXPORT jlong JNICALL
+Java_jdk_internal_jimage_ImageNativeSubstrate_getIndexAddress(JNIEnv *env,
+                jclass cls, jlong id) {
+ return JVM_ImageGetIndexAddress(env, id);
+}
+
+JNIEXPORT jlong JNICALL
+Java_jdk_internal_jimage_ImageNativeSubstrate_getDataAddress(JNIEnv *env,
+                jclass cls, jlong id) {
+ return JVM_ImageGetDataAddress(env, id);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_jdk_internal_jimage_ImageNativeSubstrate_read(JNIEnv *env,
+                                        jclass cls, jlong id, jlong offset,
+        jobject uncompressedBuffer, jlong uncompressed_size) {
+    unsigned char* uncompressedAddress;
+
+    uncompressedAddress = (unsigned char*) (*env)->GetDirectBufferAddress(env, uncompressedBuffer);
+    if (uncompressedBuffer == NULL) {
+      return JNI_FALSE;
+    }
+    return JVM_ImageRead(env, id, offset, uncompressedAddress, uncompressed_size);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_jdk_internal_jimage_ImageNativeSubstrate_readCompressed(JNIEnv *env,
+                                        jclass cls, jlong id, jlong offset,
+                                        jobject compressedBuffer, jlong compressed_size,
+        jobject uncompressedBuffer, jlong uncompressed_size) {
+    // Get address of read direct buffer.
+    unsigned char* compressedAddress;
+    unsigned char* uncompressedAddress;
+
+    compressedAddress = (unsigned char*) (*env)->GetDirectBufferAddress(env, compressedBuffer);
+    // Get address of decompression direct buffer.
+    uncompressedAddress = (unsigned char*) (*env)->GetDirectBufferAddress(env, uncompressedBuffer);
+    if (uncompressedBuffer == NULL || compressedBuffer == NULL) {
+      return JNI_FALSE;
+    }
+    return JVM_ImageReadCompressed(env, id, offset, compressedAddress, compressed_size,
+            uncompressedAddress, uncompressed_size);
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_jdk_internal_jimage_ImageNativeSubstrate_getStringBytes(JNIEnv *env,
+                                        jclass cls, jlong id, jint offset) {
+    const char* data;
+    size_t size;
+    jbyteArray byteArray;
+    jbyte* rawBytes;
+
+    data = JVM_ImageGetStringBytes(env, id, offset);
+    // Determine String length.
+    size = strlen(data);
+    // Allocate byte array.
+    byteArray = (*env)->NewByteArray(env, (jsize) size);
+    // Get array base address.
+    rawBytes = (*env)->GetByteArrayElements(env, byteArray, NULL);
+    // Copy bytes from image string table.
+    memcpy(rawBytes, data, size);
+    // Release byte array base address.
+    (*env)->ReleaseByteArrayElements(env, byteArray, rawBytes, 0);
+    return byteArray;
+}
+
+JNIEXPORT jlongArray JNICALL
+Java_jdk_internal_jimage_ImageNativeSubstrate_getAttributes(JNIEnv *env,
+        jclass cls, jlong id, jint offset) {
+    // Allocate a jlong large enough for all location attributes.
+    jlongArray attributes;
+    jlong* rawAttributes;
+    jlong* ret;
+
+    attributes = (*env)->NewLongArray(env, JVM_ImageGetAttributesCount(env));
+    // Get base address for jlong array.
+    rawAttributes = (*env)->GetLongArrayElements(env, attributes, NULL);
+    ret = JVM_ImageGetAttributes(env, rawAttributes, id, offset);
+    // Release jlong array base address.
+    (*env)->ReleaseLongArrayElements(env, attributes, rawAttributes, 0);
+    return ret == NULL ? NULL : attributes;
+}
+
+JNIEXPORT jlongArray JNICALL
+Java_jdk_internal_jimage_ImageNativeSubstrate_findAttributes(JNIEnv *env,
+        jclass cls, jlong id, jbyteArray utf8) {
+    // Allocate a jlong large enough for all location attributes.
+    jsize count;
+    jlongArray attributes;
+    jlong* rawAttributes;
+    jsize size;
+    jbyte* rawBytes;
+    jlong* ret;
+
+    count = JVM_ImageGetAttributesCount(env);
+    attributes = (*env)->NewLongArray(env, JVM_ImageGetAttributesCount(env));
+    // Get base address for jlong array.
+    rawAttributes = (*env)->GetLongArrayElements(env, attributes, NULL);
+    size = (*env)->GetArrayLength(env, utf8);
+    rawBytes = (*env)->GetByteArrayElements(env, utf8, NULL);
+    ret = JVM_ImageFindAttributes(env, rawAttributes, rawBytes, size, id);
+    (*env)->ReleaseByteArrayElements(env, utf8, rawBytes, 0);
+    // Release jlong array base address.
+    (*env)->ReleaseLongArrayElements(env, attributes, rawAttributes, 0);
+    return ret == NULL ? NULL : attributes;
+
+}
+
+JNIEXPORT jintArray JNICALL
+Java_jdk_internal_jimage_ImageNativeSubstrate_attributeOffsets(JNIEnv *env,
+        jclass cls, jlong id) {
+    unsigned int length;
+    jintArray offsets;
+    jint* rawOffsets;
+    jint* ret;
+
+    length = JVM_ImageAttributeOffsetsLength(env, id);
+    offsets = (*env)->NewIntArray(env, length);
+    // Get base address of result.
+    rawOffsets = (*env)->GetIntArrayElements(env, offsets, NULL);
+    ret = JVM_ImageAttributeOffsets(env, rawOffsets, length, id);
+    if (length == 0) {
+        return NULL;
+    }
+    // Release result base address.
+    (*env)->ReleaseIntArrayElements(env, offsets, rawOffsets, 0);
+    return ret == NULL ? NULL : offsets;
+}
--- a/jdk/src/java.base/share/native/libzip/CRC32.c	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/java.base/share/native/libzip/CRC32.c	Thu Jul 09 22:46:18 2015 -0700
@@ -43,8 +43,8 @@
 }
 
 JNIEXPORT jint JNICALL
-Java_java_util_zip_CRC32_updateBytes(JNIEnv *env, jclass cls, jint crc,
-                                     jarray b, jint off, jint len)
+Java_java_util_zip_CRC32_updateBytes0(JNIEnv *env, jclass cls, jint crc,
+                                         jarray b, jint off, jint len)
 {
     Bytef *buf = (*env)->GetPrimitiveArrayCritical(env, b, 0);
     if (buf) {
@@ -61,8 +61,8 @@
 }
 
 JNIEXPORT jint JNICALL
-Java_java_util_zip_CRC32_updateByteBuffer(JNIEnv *env, jclass cls, jint crc,
-                                          jlong address, jint off, jint len)
+Java_java_util_zip_CRC32_updateByteBuffer0(JNIEnv *env, jclass cls, jint crc,
+                                              jlong address, jint off, jint len)
 {
     Bytef *buf = (Bytef *)jlong_to_ptr(address);
     if (buf) {
--- a/jdk/src/java.base/unix/native/libjava/ConcurrentPReader_md.c	Wed Jul 05 20:41:30 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-#include <unistd.h>
-#include <errno.h>
-
-#include "jni.h"
-#include "jni_util.h"
-#include "jlong.h"
-#include "jdk_internal_jimage_concurrent_ConcurrentPReader.h"
-
-#ifdef _ALLBSD_SOURCE
-  #define pread64 pread
-#endif
-
-#define RESTARTABLE(_cmd, _result) do { \
-  do { \
-    _result = _cmd; \
-  } while((_result == -1) && (errno == EINTR)); \
-} while(0)
-
-static jfieldID fd_fdID;
-
-JNIEXPORT void JNICALL
-Java_jdk_internal_jimage_concurrent_ConcurrentPReader_initIDs(JNIEnv *env, jclass clazz)
-{
-    CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor"));
-    CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, clazz, "fd", "I"));
-}
-
-JNIEXPORT jint JNICALL
-Java_jdk_internal_jimage_concurrent_ConcurrentPReader_pread(JNIEnv *env, jclass clazz,
-                                                            jobject fdo, jlong address,
-                                                            jint len, jlong offset)
-{
-    jint fd = (*env)->GetIntField(env, fdo, fd_fdID);
-    void *buf = (void *)jlong_to_ptr(address);
-    int res;
-    RESTARTABLE(pread64(fd, buf, len, offset), res);
-    if (res == -1) {
-        JNU_ThrowIOExceptionWithLastError(env, "pread failed");
-    }
-    return res;
-}
--- a/jdk/src/java.base/windows/native/libjava/ConcurrentPReader_md.c	Wed Jul 05 20:41:30 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-#include <windows.h>
-
-#include "jni_util.h"
-#include "jlong.h"
-#include "jdk_internal_jimage_concurrent_ConcurrentPReader.h"
-
-static jfieldID handle_fdID;
-
-JNIEXPORT void JNICALL
-Java_jdk_internal_jimage_concurrent_ConcurrentPReader_initIDs(JNIEnv *env, jclass clazz)
-{
-    CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor"));
-    CHECK_NULL(handle_fdID = (*env)->GetFieldID(env, clazz, "handle", "J"));
-}
-
-JNIEXPORT jint JNICALL
-Java_jdk_internal_jimage_concurrent_ConcurrentPReader_pread(JNIEnv *env, jclass clazz,
-                                                            jobject fdo, jlong address,
-                                                            jint len, jlong offset)
-{
-    OVERLAPPED ov;
-    DWORD nread;
-    BOOL result;
-
-    HANDLE handle = (HANDLE)(*env)->GetLongField(env, fdo, handle_fdID);
-    void *buf = (void *)jlong_to_ptr(address);
-
-    ZeroMemory(&ov, sizeof(ov));
-    ov.Offset = (DWORD)offset;
-    ov.OffsetHigh = (DWORD)(offset >> 32);
-
-    result = ReadFile(handle, (LPVOID)buf, len, &nread, &ov);
-    if (result == 0) {
-        JNU_ThrowIOExceptionWithLastError(env, "ReadFile failed");
-    }
-
-    return nread;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.dev/share/classes/jdk/tools/jimage/ExtractedImage.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.tools.jimage;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import jdk.internal.jimage.Archive;
+import jdk.internal.jimage.ImageFileCreator;
+import jdk.internal.jimage.ImageModuleData;
+import jdk.internal.jimage.ImageModuleDataWriter;
+
+/**
+ *
+ * Support for extracted image.
+ */
+public final class ExtractedImage {
+
+    /**
+     * An Archive backed by a directory.
+     */
+    public class DirArchive implements Archive {
+
+        /**
+         * A File located in a Directory.
+         */
+        private class FileEntry extends Archive.Entry {
+
+            private final long size;
+            private final Path path;
+
+            FileEntry(Path path, String name) {
+                super(DirArchive.this, getPathName(path), name,
+                        Archive.Entry.EntryType.CLASS_OR_RESOURCE);
+                this.path = path;
+                try {
+                    size = Files.size(path);
+                } catch (IOException ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+
+            /**
+             * Returns the number of bytes of this file.
+             */
+            @Override
+            public long size() {
+                return size;
+            }
+
+            @Override
+            public InputStream stream() throws IOException {
+                InputStream stream = Files.newInputStream(path);
+                open.add(stream);
+                return stream;
+            }
+        }
+
+        private final Path dirPath;
+        private final String moduleName;
+        private final List<InputStream> open = new ArrayList<>();
+        private final int chop;
+
+        protected DirArchive(Path dirPath) throws IOException {
+            if (!Files.isDirectory(dirPath)) {
+                throw new IOException("Not a directory");
+            }
+            chop = dirPath.toString().length() + 1;
+            this.moduleName = dirPath.getFileName().toString();
+            System.out.println("Module name " + this.moduleName);
+            this.dirPath = dirPath;
+        }
+
+        @Override
+        public String moduleName() {
+            return moduleName;
+        }
+
+        @Override
+        public Stream<Entry> entries() {
+            try {
+                return Files.walk(dirPath).map(this::toEntry).filter(n -> n != null);
+            } catch(IOException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        private Archive.Entry toEntry(Path p) {
+            if (Files.isDirectory(p)) {
+                return null;
+            }
+            String name = getPathName(p).substring(chop);
+            if (name.startsWith("_")) {
+                return null;
+            }
+            if (verbose) {
+                String verboseName = moduleName + "/" + name;
+                log.println(verboseName);
+            }
+
+            return new FileEntry(p, name);
+        }
+
+        @Override
+        public void close() throws IOException {
+            IOException e = null;
+            for (InputStream stream : open) {
+                try {
+                    stream.close();
+                } catch (IOException ex) {
+                    if (e == null) {
+                        e = ex;
+                    } else {
+                        e.addSuppressed(ex);
+                    }
+                }
+            }
+            if (e != null) {
+                throw e;
+            }
+        }
+
+        @Override
+        public void open() throws IOException {
+            // NOOP
+        }
+    }
+    private Map<String, Set<String>> modulePackages = new LinkedHashMap<>();
+    private Set<Archive> archives = new HashSet<>();
+    private final PrintWriter log;
+    private final boolean verbose;
+
+    ExtractedImage(Path dirPath, PrintWriter log,
+            boolean verbose) throws IOException {
+        if (!Files.isDirectory(dirPath)) {
+            throw new IOException("Not a directory");
+        }
+        Files.walk(dirPath, 1).forEach((p) -> {
+            try {
+                if (!dirPath.equals(p)) {
+                    String name = getPathName(p);
+                    if (name.endsWith(ImageModuleData.META_DATA_EXTENSION)) {
+                        List<String> lines = Files.readAllLines(p);
+                        for (Entry<String, List<String>> entry
+                                : ImageModuleDataWriter.toModulePackages(lines).entrySet()) {
+                            Set<String> pkgs = new HashSet<>();
+                            pkgs.addAll(entry.getValue());
+                            modulePackages.put(entry.getKey(), pkgs);
+                        }
+                        modulePackages = Collections.unmodifiableMap(modulePackages);
+                    } else {
+                        if (Files.isDirectory(p)) {
+                            Archive a = new DirArchive(p);
+                            archives.add(a);
+                        }
+                    }
+                }
+            } catch (IOException ex) {
+                throw new RuntimeException(ex);
+            }
+        });
+        archives = Collections.unmodifiableSet(archives);
+        this.log = log;
+        this.verbose = verbose;
+    }
+
+    void recreateJImage(Path path) throws IOException {
+
+        ImageFileCreator.recreateJimage(path, archives, modulePackages);
+    }
+
+    private static String getPathName(Path path) {
+        return path.toString().replace(File.separatorChar, '/');
+    }
+}
--- a/jdk/src/jdk.dev/share/classes/jdk/tools/jimage/JImageTask.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/jdk.dev/share/classes/jdk/tools/jimage/JImageTask.java	Thu Jul 09 22:46:18 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,141 +25,98 @@
 
 package jdk.tools.jimage;
 
-import java.io.BufferedOutputStream;
-import java.io.DataOutputStream;
 import java.io.File;
 import java.io.IOException;
-import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.text.MessageFormat;
-import java.util.ArrayList;
+import static java.nio.file.StandardOpenOption.READ;
+import static java.nio.file.StandardOpenOption.WRITE;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Locale;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 import jdk.internal.jimage.BasicImageReader;
-import jdk.internal.jimage.BasicImageWriter;
 import jdk.internal.jimage.ImageHeader;
+import static jdk.internal.jimage.ImageHeader.MAGIC;
+import static jdk.internal.jimage.ImageHeader.MAJOR_VERSION;
+import static jdk.internal.jimage.ImageHeader.MINOR_VERSION;
 import jdk.internal.jimage.ImageLocation;
-import jdk.internal.jimage.PackageModuleMap;
+import jdk.internal.jimage.ImageModuleData;
+import jdk.internal.jimage.ImageResourcesTree;
+import jdk.tools.jimage.TaskHelper.BadArgs;
+import jdk.tools.jimage.TaskHelper.HiddenOption;
+import jdk.tools.jimage.TaskHelper.Option;
+import jdk.tools.jimage.TaskHelper.OptionsHelper;
 
 class JImageTask {
-    static class BadArgs extends Exception {
-        static final long serialVersionUID = 8765093759964640723L;  // ## re-generate
-        final String key;
-        final Object[] args;
-        boolean showUsage;
 
-        BadArgs(String key, Object... args) {
-            super(JImageTask.getMessage(key, args));
-            this.key = key;
-            this.args = args;
-        }
-
-        BadArgs showUsage(boolean b) {
-            showUsage = b;
-            return this;
-        }
-    }
-
-    static abstract class Option {
-        final boolean hasArg;
-        final String[] aliases;
-
-        Option(boolean hasArg, String... aliases) {
-            this.hasArg = hasArg;
-            this.aliases = aliases;
-        }
-
-        boolean isHidden() {
-            return false;
-        }
-
-        boolean matches(String opt) {
-            for (String a : aliases) {
-                if (a.equals(opt)) {
-                    return true;
-                } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        boolean ignoreRest() {
-            return false;
-        }
-
-        abstract void process(JImageTask task, String opt, String arg) throws BadArgs;
-    }
-
-    static abstract class HiddenOption extends Option {
-        HiddenOption(boolean hasArg, String... aliases) {
-            super(hasArg, aliases);
-        }
-
-        @Override
-        boolean isHidden() {
-            return true;
-        }
-    }
-
-    static Option[] recognizedOptions = {
-        new Option(true, "--dir") {
+    static final Option<?>[] recognizedOptions = {
+        new Option<JImageTask>(true, "--dir") {
             @Override
-            void process(JImageTask task, String opt, String arg) throws BadArgs {
+            protected void process(JImageTask task, String opt, String arg) throws BadArgs {
                  task.options.directory = arg;
             }
         },
-        new HiddenOption(false, "--fullversion") {
+        new HiddenOption<JImageTask>(false, "--fullversion") {
             @Override
-            void process(JImageTask task, String opt, String arg) {
+            protected void process(JImageTask task, String opt, String arg) {
                 task.options.fullVersion = true;
             }
         },
-        new Option(false, "--help") {
+        new Option<JImageTask>(false, "--help") {
             @Override
-            void process(JImageTask task, String opt, String arg) {
+            protected void process(JImageTask task, String opt, String arg) {
                 task.options.help = true;
             }
         },
-        new Option(false, "--verbose") {
+
+        new Option<JImageTask>(true, "--flags") {
             @Override
-            void process(JImageTask task, String opt, String arg) throws BadArgs {
+            protected void process(JImageTask task, String opt, String arg) {
+                task.options.flags = arg;
+            }
+        },
+
+        new Option<JImageTask>(false, "--verbose") {
+            @Override
+            protected void process(JImageTask task, String opt, String arg) throws BadArgs {
                  task.options.verbose = true;
             }
         },
-        new Option(false, "--version") {
+        new Option<JImageTask>(false, "--version") {
             @Override
-            void process(JImageTask task, String opt, String arg) {
+            protected void process(JImageTask task, String opt, String arg) {
                 task.options.version = true;
             }
         },
     };
+    private static final TaskHelper taskHelper
+            = new TaskHelper("jdk.tools.jimage.resources.jimage");
+    private static final OptionsHelper<JImageTask> optionsHelper
+            = taskHelper.newOptionsHelper(JImageTask.class, recognizedOptions);
 
-    static class Options {
+    static class OptionsValues {
         Task task = Task.LIST;
         String directory = ".";
         boolean fullVersion;
         boolean help;
+        String flags;
         boolean verbose;
         boolean version;
         List<File> jimages = new LinkedList<>();
     }
 
     private static final String PROGNAME = "jimage";
-    private final Options options = new Options();
+    private final OptionsValues options = new OptionsValues();
 
     enum Task {
-        RECREATE,
         EXTRACT,
         INFO,
         LIST,
+        RECREATE,
+        SET,
         VERIFY
     };
 
@@ -210,23 +167,29 @@
 
     int run(String[] args) {
         if (log == null) {
-            log = new PrintWriter(System.out);
+            setLog(new PrintWriter(System.out));
         }
 
         try {
-            handleOptions(args);
+            List<String> unhandled = optionsHelper.handleOptions(this, args);
+            if(!unhandled.isEmpty()) {
+                options.task = Enum.valueOf(Task.class, unhandled.get(0).toUpperCase());
+                for(int i = 1; i < unhandled.size(); i++) {
+                    options.jimages.add(new File(unhandled.get(i)));
+                }
+            }
             if (options.help) {
-                showHelp();
+                optionsHelper.showHelp(PROGNAME, "recreate only options:");
             }
             if (options.version || options.fullVersion) {
-                showVersion(options.fullVersion);
+                taskHelper.showVersion(options.fullVersion);
             }
             boolean ok = run();
             return ok ? EXIT_OK : EXIT_ERROR;
         } catch (BadArgs e) {
-            reportError(e.key, e.args);
+            taskHelper.reportError(e.key, e.args);
             if (e.showUsage) {
-                log.println(getMessage("main.usage.summary", PROGNAME));
+                log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
             }
             return EXIT_CMDERR;
         } catch (Exception x) {
@@ -237,98 +200,26 @@
         }
     }
 
-    static final String MODULES_ENTRY = PackageModuleMap.MODULES_ENTRY;
-    static final String PACKAGES_ENTRY = "/" + PackageModuleMap.PACKAGES_ENTRY;
-
     private void recreate() throws IOException, BadArgs {
         File directory = new File(options.directory);
-        Path dirPath = directory.toPath();
-        int chop = dirPath.toString().length() + 1;
-
         if (!directory.isDirectory()) {
-            throw new BadArgs("err.not.a.dir", directory.getAbsolutePath());
+            throw taskHelper.newBadArgs("err.not.a.dir", directory.getAbsolutePath());
         }
-
+        Path dirPath = directory.toPath();
         if (options.jimages.isEmpty()) {
-            throw new BadArgs("err.jimage.not.specified");
+            throw taskHelper.newBadArgs("err.jimage.not.specified");
         } else if (options.jimages.size() != 1) {
-            throw new BadArgs("err.only.one.jimage");
+            throw taskHelper.newBadArgs("err.only.one.jimage");
         }
 
-        File jimage = options.jimages.get(0);
-        final List<File> files = new ArrayList<>();
-        final BasicImageWriter writer = new BasicImageWriter();
-        final Long longZero = 0L;
-
-        // Note: code sensitive to Netbeans parser crashing.
-        long total = Files.walk(dirPath).reduce(longZero, (Long offset, Path path) -> {
-                    long size = 0;
-                    String pathString = path.toString();
-
-                    if (pathString.length() < chop || pathString.startsWith(".")) {
-                        return 0L;
-                    }
-
-                    File file = path.toFile();
-
-                    if (file.isFile()) {
-                        String name = pathString.substring(chop).replace(File.separatorChar, '/');
-
-                        if (options.verbose) {
-                            log.println(name);
-                        }
-
-                        if (name.endsWith(MODULES_ENTRY) || name.endsWith(PACKAGES_ENTRY)) {
-                            try {
-                                try (Stream<String> lines = Files.lines(path)) {
-                                    size = lines.peek(s -> writer.addString(s)).count() * 4;
-                                }
-                            } catch (IOException ex) {
-                                // Caught again when writing file.
-                                size = 0;
-                            }
-                        } else {
-                            size = file.length();
-                        }
+        Path jimage = options.jimages.get(0).toPath();
 
-                        writer.addLocation(name, offset, 0L, size);
-                        files.add(file);
-                    }
-
-                    return offset + size;
-                },
-                (Long offsetL, Long offsetR) -> { return longZero; } );
-
-        if (jimage.createNewFile()) {
-            try (OutputStream os = Files.newOutputStream(jimage.toPath());
-                    BufferedOutputStream bos = new BufferedOutputStream(os);
-                    DataOutputStream out = new DataOutputStream(bos)) {
-
-                byte[] index = writer.getBytes();
-                out.write(index, 0, index.length);
-
-                for (File file : files) {
-                    try {
-                        Path path = file.toPath();
-                        String name = path.toString().replace(File.separatorChar, '/');
-
-                        if (name.endsWith(MODULES_ENTRY) || name.endsWith(PACKAGES_ENTRY)) {
-                            for (String line: Files.readAllLines(path)) {
-                                int off = writer.addString(line);
-                                out.writeInt(off);
-                            }
-                        } else {
-                            Files.copy(path, out);
-                        }
-                    } catch (IOException ex) {
-                        throw new BadArgs("err.cannot.read.file", file.getName());
-                    }
-                }
-            }
+        if (jimage.toFile().createNewFile()) {
+            ExtractedImage img = new ExtractedImage(dirPath, log, options.verbose);
+            img.recreateJImage(jimage);
         } else {
-            throw new BadArgs("err.jimage.already.exists", jimage.getName());
+            throw taskHelper.newBadArgs("err.jimage.already.exists", jimage.getFileName());
         }
-
     }
 
     private void title(File file, BasicImageReader reader) {
@@ -351,10 +242,12 @@
     }
 
     private interface ResourceAction {
-        public void apply(BasicImageReader reader, String name, ImageLocation location) throws IOException, BadArgs;
+        public void apply(BasicImageReader reader, String name,
+                ImageLocation location) throws IOException, BadArgs;
     }
 
-    private void extract(BasicImageReader reader, String name, ImageLocation location) throws IOException, BadArgs {
+    private void extract(BasicImageReader reader, String name,
+            ImageLocation location) throws IOException, BadArgs {
         File directory = new File(options.directory);
         byte[] bytes = reader.getResource(location);
         File resource =  new File(directory, name);
@@ -362,21 +255,23 @@
 
         if (parent.exists()) {
             if (!parent.isDirectory()) {
-                throw new BadArgs("err.cannot.create.dir", parent.getAbsolutePath());
+                throw taskHelper.newBadArgs("err.cannot.create.dir", parent.getAbsolutePath());
             }
         } else if (!parent.mkdirs()) {
-            throw new BadArgs("err.cannot.create.dir", parent.getAbsolutePath());
+            throw taskHelper.newBadArgs("err.cannot.create.dir", parent.getAbsolutePath());
         }
 
-        if (name.endsWith(MODULES_ENTRY) || name.endsWith(PACKAGES_ENTRY)) {
-            List<String> names = reader.getNames(bytes);
-            Files.write(resource.toPath(), names);
+        if (name.endsWith(ImageModuleData.META_DATA_EXTENSION)) {
+            ImageModuleData imageModuleData = new ImageModuleData(reader, bytes);
+            List<String> lines = imageModuleData.fromModulePackages();
+            Files.write(resource.toPath(), lines);
         } else {
-            Files.write(resource.toPath(), bytes);
+            if (!ImageResourcesTree.isTreeInfoResource(name)) {
+                Files.write(resource.toPath(), bytes);
+            }
         }
     }
 
-    private static final int NAME_WIDTH = 40;
     private static final int NUMBER_WIDTH = 12;
     private static final int OFFSET_WIDTH = NUMBER_WIDTH;
     private static final int SIZE_WIDTH = NUMBER_WIDTH;
@@ -397,12 +292,14 @@
         }
     }
 
-    private void info(File file, BasicImageReader reader) {
+    private void info(File file, BasicImageReader reader) throws IOException {
         ImageHeader header = reader.getHeader();
 
         log.println(" Major Version:  " + header.getMajorVersion());
         log.println(" Minor Version:  " + header.getMinorVersion());
-        log.println(" Location Count: " + header.getLocationCount());
+        log.println(" Flags:          " + Integer.toHexString(header.getMinorVersion()));
+        log.println(" Resource Count: " + header.getResourceCount());
+        log.println(" Table Length:   " + header.getTableLength());
         log.println(" Offsets Size:   " + header.getOffsetsSize());
         log.println(" Redirects Size: " + header.getRedirectSize());
         log.println(" Locations Size: " + header.getLocationsSize());
@@ -414,16 +311,39 @@
         print(reader, name);
     }
 
-    void verify(BasicImageReader reader, String name, ImageLocation location) {
-        if (name.endsWith(".class")) {
-            byte[] bytes;
+    void set(File file, BasicImageReader reader) throws BadArgs {
+        try {
+            ImageHeader oldHeader = reader.getHeader();
+
+            int value = 0;
             try {
-                bytes = reader.getResource(location);
-            } catch (IOException ex) {
-                log.println(ex);
-                bytes = null;
+                value = Integer.valueOf(options.flags);
+            } catch (NumberFormatException ex) {
+                throw taskHelper.newBadArgs("err.flags.not.int", options.flags);
             }
 
+            ImageHeader newHeader = new ImageHeader(MAGIC, MAJOR_VERSION, MINOR_VERSION,
+                    value,
+                    oldHeader.getResourceCount(), oldHeader.getTableLength(),
+                    oldHeader.getLocationsSize(), oldHeader.getStringsSize());
+
+            ByteBuffer buffer = ByteBuffer.allocate(ImageHeader.getHeaderSize());
+            buffer.order(ByteOrder.nativeOrder());
+            newHeader.writeTo(buffer);
+            buffer.rewind();
+
+            try (FileChannel channel = FileChannel.open(file.toPath(), READ, WRITE)) {
+                channel.write(buffer, 0);
+            }
+        } catch (IOException ex) {
+            throw taskHelper.newBadArgs("err.cannot.update.file", file.getName());
+        }
+    }
+
+     void verify(BasicImageReader reader, String name, ImageLocation location) {
+        if (name.endsWith(".class")) {
+            byte[] bytes = reader.getResource(location);
+
             if (bytes == null || bytes.length <= 4 ||
                 (bytes[0] & 0xFF) != 0xCA ||
                 (bytes[1] & 0xFF) != 0xFE ||
@@ -435,10 +355,11 @@
         }
     }
 
-    private void iterate(JImageAction jimageAction, ResourceAction resourceAction) throws IOException, BadArgs {
+    private void iterate(JImageAction jimageAction,
+            ResourceAction resourceAction) throws IOException, BadArgs {
         for (File file : options.jimages) {
             if (!file.exists() || !file.isFile()) {
-                throw new BadArgs("err.not.a.jimage", file.getName());
+                throw taskHelper.newBadArgs("err.not.a.jimage", file.getName());
             }
 
             String path = file.getCanonicalPath();
@@ -449,11 +370,13 @@
             }
 
             if (resourceAction != null) {
-                String[] entryNames = reader.getEntryNames(true);
+                String[] entryNames = reader.getEntryNames();
 
                 for (String name : entryNames) {
-                    ImageLocation location = reader.findLocation(name);
-                    resourceAction.apply(reader, name, location);
+                    if (!ImageResourcesTree.isTreeInfoResource(name)) {
+                        ImageLocation location = reader.findLocation(name);
+                        resourceAction.apply(reader, name, location);
+                    }
                 }
             }
        }
@@ -461,9 +384,6 @@
 
     private boolean run() throws IOException, BadArgs {
         switch (options.task) {
-            case RECREATE:
-                recreate();
-                break;
             case EXTRACT:
                 iterate(null, this::extract);
                 break;
@@ -473,11 +393,17 @@
             case LIST:
                 iterate(this::listTitle, this::list);
                 break;
+            case RECREATE:
+                recreate();
+                break;
+            case SET:
+                iterate(this::set, null);
+                break;
             case VERIFY:
                 iterate(this::title, this::verify);
                 break;
             default:
-                throw new BadArgs("err.invalid.task", options.task.name()).showUsage(true);
+                throw taskHelper.newBadArgs("err.invalid.task", options.task.name()).showUsage(true);
         }
         return true;
     }
@@ -485,112 +411,6 @@
     private PrintWriter log;
     void setLog(PrintWriter out) {
         log = out;
-    }
-    public void handleOptions(String[] args) throws BadArgs {
-        // process options
-        int first = 0;
-
-        if (args.length == 0) {
-            return;
-        }
-
-        String arg = args[first];
-
-        if (!arg.startsWith("-")) {
-            try {
-                options.task = Enum.valueOf(Task.class, arg.toUpperCase());
-                first++;
-            } catch (IllegalArgumentException e) {
-                throw new BadArgs("err.invalid.task", arg).showUsage(true);
-            }
-        }
-
-        for (int i = first; i < args.length; i++) {
-            arg = args[i];
-
-            if (arg.charAt(0) == '-') {
-                Option option = getOption(arg);
-                String param = null;
-
-                if (option.hasArg) {
-                    if (arg.startsWith("--") && arg.indexOf('=') > 0) {
-                        param = arg.substring(arg.indexOf('=') + 1, arg.length());
-                    } else if (i + 1 < args.length) {
-                        param = args[++i];
-                    }
-
-                    if (param == null || param.isEmpty() || param.charAt(0) == '-') {
-                        throw new BadArgs("err.missing.arg", arg).showUsage(true);
-                    }
-                }
-
-                option.process(this, arg, param);
-
-                if (option.ignoreRest()) {
-                    i = args.length;
-                }
-            } else {
-                File file = new File(arg);
-                options.jimages.add(file);
-            }
-        }
-    }
-
-    private Option getOption(String name) throws BadArgs {
-        for (Option o : recognizedOptions) {
-            if (o.matches(name)) {
-                return o;
-            }
-        }
-        throw new BadArgs("err.unknown.option", name).showUsage(true);
-    }
-
-    private void reportError(String key, Object... args) {
-        log.println(getMessage("error.prefix") + " " + getMessage(key, args));
-    }
-
-    private void warning(String key, Object... args) {
-        log.println(getMessage("warn.prefix") + " " + getMessage(key, args));
-    }
-
-    private void showHelp() {
-        log.println(getMessage("main.usage", PROGNAME));
-        for (Option o : recognizedOptions) {
-            String name = o.aliases[0].substring(1); // there must always be at least one name
-            name = name.charAt(0) == '-' ? name.substring(1) : name;
-            if (o.isHidden() || name.equals("h")) {
-                continue;
-            }
-            log.println(getMessage("main.opt." + name));
-        }
-    }
-
-    private void showVersion(boolean full) {
-        log.println(version(full ? "full" : "release"));
-    }
-
-    private String version(String key) {
-        return System.getProperty("java.version");
-    }
-
-    static String getMessage(String key, Object... args) {
-        try {
-            return MessageFormat.format(ResourceBundleHelper.bundle.getString(key), args);
-        } catch (MissingResourceException e) {
-            throw new InternalError("Missing message: " + key);
-        }
-    }
-
-    private static class ResourceBundleHelper {
-        static final ResourceBundle bundle;
-
-        static {
-            Locale locale = Locale.getDefault();
-            try {
-                bundle = ResourceBundle.getBundle("jdk.tools.jimage.resources.jimage", locale);
-            } catch (MissingResourceException e) {
-                throw new InternalError("Cannot find jimage resource bundle for locale " + locale);
-            }
-        }
+        taskHelper.setLog(log);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.dev/share/classes/jdk/tools/jimage/TaskHelper.java	Thu Jul 09 22:46:18 2015 -0700
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.tools.jimage;
+
+import java.io.PrintWriter;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * JImage tools shared helper.
+ */
+public final class TaskHelper {
+
+    public class BadArgs extends Exception {
+
+        static final long serialVersionUID = 8765093759964640721L;
+
+        private BadArgs(String key, Object... args) {
+            super(bundleHelper.getMessage(key, args));
+            this.key = key;
+            this.args = args;
+        }
+
+        public BadArgs showUsage(boolean b) {
+            showUsage = b;
+            return this;
+        }
+        public final String key;
+        public final Object[] args;
+        public boolean showUsage;
+    }
+
+    public static abstract class Option<T> {
+
+        final boolean hasArg;
+        final String[] aliases;
+
+        public Option(boolean hasArg, String... aliases) {
+            this.hasArg = hasArg;
+            this.aliases = aliases;
+        }
+
+        public boolean isHidden() {
+            return false;
+        }
+
+        public boolean matches(String opt) {
+            for (String a : aliases) {
+                if (a.equals(opt)) {
+                    return true;
+                } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public boolean ignoreRest() {
+            return false;
+        }
+
+        protected abstract void process(T task, String opt, String arg) throws BadArgs;
+    }
+
+    public static abstract class HiddenOption<T> extends Option<T> {
+
+        public HiddenOption(boolean hasArg, String... aliases) {
+            super(hasArg, aliases);
+        }
+
+        @Override
+        public boolean isHidden() {
+            return true;
+        }
+    }
+
+    private class ResourceBundleHelper {
+
+        private final ResourceBundle bundle;
+
+        ResourceBundleHelper(String path) {
+            Locale locale = Locale.getDefault();
+            try {
+                bundle = ResourceBundle.getBundle(path, locale);
+            } catch (MissingResourceException e) {
+                throw new InternalError("Cannot find resource bundle for locale " + locale);
+            }
+        }
+
+        String getMessage(String key, Object... args) {
+            String val = bundle.getString(key);
+            return MessageFormat.format(val, args);
+        }
+
+    }
+
+    public class OptionsHelper<T> {
+
+        private final List<Option<T>> options;
+
+        OptionsHelper(List<Option<T>> options) {
+            this.options = options;
+        }
+
+        public List<String> handleOptions(T task, String[] args) throws BadArgs {
+            List<String> rest = new ArrayList<>();
+            // process options
+            for (int i = 0; i < args.length; i++) {
+                if (args[i].charAt(0) == '-') {
+                    String name = args[i];
+                    Option<T> option = getOption(name);
+                    if (option == null) {
+                        throw new BadArgs("err.unknown.option", name).showUsage(true);
+                    }
+                    String param = null;
+                    if (option.hasArg) {
+                        if (name.startsWith("--") && name.indexOf('=') > 0) {
+                            param = name.substring(name.indexOf('=') + 1, name.length());
+                        } else if (i + 1 < args.length) {
+                            param = args[++i];
+                        }
+                        if (param == null || param.isEmpty() || param.charAt(0) == '-') {
+                            throw new BadArgs("err.missing.arg", name).showUsage(true);
+                        }
+                    }
+                    option.process(task, name, param);
+                    if (option.ignoreRest()) {
+                        i = args.length;
+                    }
+                } else {
+                    rest.add(args[i]);
+                }
+            }
+            return rest;
+        }
+
+        private Option<T> getOption(String name) throws BadArgs {
+            for (Option<T> o : options) {
+                if (o.matches(name)) {
+                    return o;
+                }
+            }
+            return null;
+        }
+
+        public void showHelp(String progName, String pluginsHeader) {
+            log.println(bundleHelper.getMessage("main.usage", progName));
+            for (Option<?> o : options) {
+                String name = o.aliases[0].substring(1); // there must always be at least one name
+                name = name.charAt(0) == '-' ? name.substring(1) : name;
+                if (o.isHidden() || name.equals("h")) {
+                    continue;
+                }
+                log.println(bundleHelper.getMessage("main.opt." + name));
+            }
+        }
+    }
+
+    private PrintWriter log;
+    private final ResourceBundleHelper bundleHelper;
+
+    public TaskHelper(String path) {
+        this.bundleHelper = new ResourceBundleHelper(path);
+    }
+
+    public <T> OptionsHelper<T> newOptionsHelper(Class<T> clazz, Option<?>[] options) {
+        List<Option<T>> optionsList = new ArrayList<>();
+        for (Option<?> o : options) {
+            @SuppressWarnings("unchecked")
+            Option<T> opt = (Option<T>) o;
+            optionsList.add(opt);
+        }
+        return new OptionsHelper<>(optionsList);
+    }
+
+    public BadArgs newBadArgs(String key, Object... args) {
+        return new BadArgs(key, args);
+    }
+
+    public String getMessage(String key, Object... args) {
+        return bundleHelper.getMessage(key, args);
+    }
+
+    public void setLog(PrintWriter log) {
+        this.log = log;
+    }
+
+    public void reportError(String key, Object... args) {
+        log.println(bundleHelper.getMessage("error.prefix") + " " + bundleHelper.getMessage(key, args));
+    }
+
+    public void warning(String key, Object... args) {
+        log.println(bundleHelper.getMessage("warn.prefix") + " " + bundleHelper.getMessage(key, args));
+    }
+
+    public void showVersion(boolean full) {
+        log.println(version(full ? "full" : "release"));
+    }
+
+    public String version(String key) {
+        return System.getProperty("java.version");
+    }
+
+}
--- a/jdk/src/jdk.dev/share/classes/jdk/tools/jimage/resources/jimage.properties	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/jdk.dev/share/classes/jdk/tools/jimage/resources/jimage.properties	Thu Jul 09 22:46:18 2015 -0700
@@ -1,16 +1,17 @@
 main.usage.summary=\
-Usage: {0} <extract|recreate|info|list|verify> <options> jimage...\n\
+Usage: {0} <extract|info|list|recreate|set|verify> <options> jimage...\n\
 use --help for a list of possible options
 
 main.usage=\
-Usage: {0} <extract|recreate|info|list|verify> <options> jimage...\n\
+Usage: {0} <extract|info|list|recreate|set|verify> <options> jimage...\n\
 \n\
 \  extract  - Extract all jimage entries into separate files into the directory\n\
 \             specified by --dir=<directory> (default='.')\n\
-\  recreate - Reconstructs a jimage from an extracted directory (--dir)\n\
 \  info     - Prints information specified in the jimage header.\n\
 \  list     - Prints the names of all the entries in the jimage.  When used with\n\
 \             --verbose will also print entry attributes ex. size and offset.\n\
+\  recreate - Reconstructs a jimage from an extracted directory (--dir)\n\
+\  set      - sets the value of specific jimage header entries\n\
 \  verify   - Reports errors on any .class entries that don't verify as classes.\n\
 \n\
 Possible options include:
@@ -19,27 +20,32 @@
 warn.prefix=Warning:
 
 main.opt.dir=\
-\  --dir                                Target directory for create/expand
+\  --dir                                Target directory for extract/recreate
+
+main.opt.flags=\
+\  --flags=value                        Set the jimage flags to value
+
+main.opt.help=\
+\  --help                               Print this usage message
 
 main.opt.verbose=\
 \  --verbose                            Verbose listing
 
-main.opt.help=\
-\  --help                               Print this usage message
-
 main.opt.version=\
 \  --version                            Version information
 
-err.invalid.task=task must be list|expand|info|verify: {0}
-err.not.a.dir=not a directory: {0}
-err.jimage.not.specified=no jimage specified
-err.only.one.jimage=only one jimage should be specified
-err.jimage.already.exists=jimage already exists: {0}
+err.cannot.create.dir=cannot create directory: {0}
 err.cannot.read.file=cannot read file: {0}
-err.cannot.create.dir=cannot create directory: {0}
-err.not.a.jimage=not a jimage file: {0}
-err.unknown.option=unknown option: {0}
-err.missing.arg=no value given for {0}
+err.cannot.update.file=cannot update file: {0}
+err.flags.not.int=--flags value not integer: {0}
 err.internal.error=internal error: {0} {1} {2}
 err.invalid.arg.for.option=invalid argument for option: {0}
+err.invalid.task=task must be extract|recreate|info|list|verify: {0}
+err.jimage.already.exists=jimage already exists: {0}
+err.jimage.not.specified=no jimage specified
+err.missing.arg=no value given for {0}
+err.not.a.dir=not a directory: {0}
+err.not.a.jimage=not a jimage file: {0}
+err.only.one.jimage=only one jimage should be specified
 err.option.unsupported={0} not supported: {1}
+err.unknown.option=unknown option: {0}
--- a/jdk/src/jdk.rmic/share/classes/sun/tools/java/ClassPath.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/src/jdk.rmic/share/classes/sun/tools/java/ClassPath.java	Thu Jul 09 22:46:18 2015 -0700
@@ -394,7 +394,7 @@
         this.pkgDirs = new HashMap<>();
 
         // fill in module directories at the root dir
-        Path root = fs.getPath("/");
+        Path root = fs.getPath("/modules");
         try {
             try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) {
                 for (Path entry: stream) {
--- a/jdk/test/com/sun/jdi/cds/CDSBreakpointTest.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/com/sun/jdi/cds/CDSBreakpointTest.java	Thu Jul 09 22:46:18 2015 -0700
@@ -31,6 +31,9 @@
  *          jdk.jartool/sun.tools.jar
  * @library /lib/testlibrary
  * @library ..
+ * @build jdk.testlibrary.*
+ * @build TestScaffold VMConnection TargetListener TargetAdapter
+ * @build CDSJDITest
  * @run compile -g ../BreakpointTest.java
  * @run main CDSBreakpointTest
  */
--- a/jdk/test/com/sun/jdi/cds/CDSDeleteAllBkptsTest.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/com/sun/jdi/cds/CDSDeleteAllBkptsTest.java	Thu Jul 09 22:46:18 2015 -0700
@@ -31,6 +31,9 @@
  *          jdk.jartool/sun.tools.jar
  * @library /lib/testlibrary
  * @library ..
+ * @build jdk.testlibrary.*
+ * @build TestScaffold VMConnection TargetListener TargetAdapter
+ * @build CDSJDITest
  * @run compile -g ../DeleteAllBkptsTest.java
  * @run main CDSDeleteAllBkptsTest
  */
--- a/jdk/test/com/sun/jdi/cds/CDSFieldWatchpoints.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/com/sun/jdi/cds/CDSFieldWatchpoints.java	Thu Jul 09 22:46:18 2015 -0700
@@ -31,6 +31,9 @@
  *          jdk.jartool/sun.tools.jar
  * @library /lib/testlibrary
  * @library ..
+ * @build jdk.testlibrary.*
+ * @build TestScaffold VMConnection TargetListener TargetAdapter
+ * @build CDSJDITest
  * @run compile -g ../FieldWatchpoints.java
  * @run main CDSFieldWatchpoints
  */
--- a/jdk/test/java/lang/Class/getDeclaredField/FieldSetAccessibleTest.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/java/lang/Class/getDeclaredField/FieldSetAccessibleTest.java	Thu Jul 09 22:46:18 2015 -0700
@@ -221,8 +221,8 @@
 
         Stream<String> build() {
             return roots.stream().flatMap(this::toStream)
-                    .filter(x -> x.getNameCount() > 1)
-                    .map( x-> x.subpath(1, x.getNameCount()))
+                    .filter(x -> x.getNameCount() > 2)
+                    .map( x-> x.subpath(2, x.getNameCount()))
                     .map( x -> x.toString())
                     .filter(s -> s.endsWith(".class"));
         }
--- a/jdk/test/java/nio/Buffer/LimitDirectMemory.sh	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/java/nio/Buffer/LimitDirectMemory.sh	Thu Jul 09 22:46:18 2015 -0700
@@ -28,6 +28,7 @@
 # @summary Test option to limit direct memory allocation
 #
 # @build LimitDirectMemory
+# @ignore JDK-8129343
 # @run shell LimitDirectMemory.sh
 
 TMP1=tmp_$$
--- a/jdk/test/java/nio/charset/Charset/NIOCharsetAvailabilityTest.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/java/nio/charset/Charset/NIOCharsetAvailabilityTest.java	Thu Jul 09 22:46:18 2015 -0700
@@ -46,9 +46,9 @@
         // two known charset implementation packages
         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
         Set<Class> charsets =
-            Stream.concat(Files.walk(fs.getPath("/java.base/sun/nio/cs/")),
-                          Files.walk(fs.getPath("/jdk.charsets/sun/nio/cs/ext/")))
-                 .map( p -> p.subpath(1, p.getNameCount()).toString())
+            Stream.concat(Files.walk(fs.getPath("/modules/java.base/sun/nio/cs/")),
+                          Files.walk(fs.getPath("/modules/jdk.charsets/sun/nio/cs/ext/")))
+                 .map( p -> p.subpath(2, p.getNameCount()).toString())
                  .filter( s ->  s.indexOf("$") == -1 && s.endsWith(".class"))
                  .map( s -> {
                      try {
--- a/jdk/test/java/nio/file/spi/SetDefaultProvider.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/java/nio/file/spi/SetDefaultProvider.java	Thu Jul 09 22:46:18 2015 -0700
@@ -25,6 +25,7 @@
  * @bug 4313887 7006126
  * @summary Unit test for java.nio.file.spi.FileSystemProvider
  * @build TestProvider SetDefaultProvider
+ * @ignore JDK-8129343
  * @run main/othervm -Djava.nio.file.spi.DefaultFileSystemProvider=TestProvider SetDefaultProvider
  */
 
--- a/jdk/test/javax/management/monitor/GaugeMonitorDeadlockTest.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/javax/management/monitor/GaugeMonitorDeadlockTest.java	Thu Jul 09 22:46:18 2015 -0700
@@ -27,6 +27,7 @@
  * @summary Test that no locks are held when a monitor attribute is sampled
  * or notif delivered.
  * @author Eamonn McManus
+ * @library /lib/testlibrary
  * @modules java.management
  * @run clean GaugeMonitorDeadlockTest
  * @run build GaugeMonitorDeadlockTest
@@ -48,6 +49,8 @@
 import javax.management.monitor.GaugeMonitor;
 import javax.management.monitor.GaugeMonitorMBean;
 
+import jdk.testlibrary.Utils;
+
 public class GaugeMonitorDeadlockTest {
     private static enum When {IN_GET_ATTRIBUTE, IN_NOTIFY};
     private static long checkingTime;
@@ -55,8 +58,7 @@
     public static void main(String[] args) throws Exception {
         if (args.length != 1)
             throw new Exception("Arg should be test number");
-        double factor = Double.parseDouble(System.getProperty("test.timeout.factor", "1.0"));
-        checkingTime = (long)factor*1000;
+        checkingTime = Utils.adjustTimeout(1000); // default 1s timeout
         System.out.println("=== checkingTime = " + checkingTime + "ms");
 
         int testNo = Integer.parseInt(args[0]) - 1;
@@ -102,11 +104,12 @@
             monitorProxy.setGranularityPeriod(10L); // 10 ms
             monitorProxy.setNotifyHigh(true);
             monitorProxy.setNotifyLow(true);
-            monitorProxy.start();
 
             System.out.println("=== Waiting observedProxy.getGetCount() to be "
                     + "changed, presumable deadlock if timeout?");
             final int initGetCount = observedProxy.getGetCount();
+            monitorProxy.start();
+
             long checkedTime = System.currentTimeMillis();
             long nowTime;
             ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
--- a/jdk/test/javax/management/monitor/StringMonitorDeadlockTest.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/javax/management/monitor/StringMonitorDeadlockTest.java	Thu Jul 09 22:46:18 2015 -0700
@@ -38,7 +38,6 @@
 
 import java.lang.management.ManagementFactory;
 import java.util.concurrent.atomic.AtomicInteger;
-import javax.management.Attribute;
 import javax.management.JMX;
 import javax.management.MBeanServer;
 import javax.management.Notification;
@@ -96,9 +95,10 @@
             monitorProxy.setStringToCompare("old");
             monitorProxy.setGranularityPeriod(10L); // 10 ms
             monitorProxy.setNotifyDiffer(true);
+
+            final int initGetCount = observedProxy.getGetCount();
             monitorProxy.start();
 
-            final int initGetCount = observedProxy.getGetCount();
             int getCount = initGetCount;
             for (int i = 0; i < 500; i++) { // 500 * 10 = 5 seconds
                 getCount = observedProxy.getGetCount();
--- a/jdk/test/jdk/internal/jimage/VerifyJimage.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/jdk/internal/jimage/VerifyJimage.java	Thu Jul 09 22:46:18 2015 -0700
@@ -217,7 +217,12 @@
         }
 
         int entries() {
-            return getHeader().getLocationCount();
+            try {
+                return getHeader().getTableLength();
+            } catch (IOException ex) {
+                failed.add(imageName() + ": can't access header");
+                return 0;
+            }
         }
 
         void compare(String entry, Path p) {
--- a/jdk/test/jdk/internal/jrtfs/Basic.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/jdk/internal/jrtfs/Basic.java	Thu Jul 09 22:46:18 2015 -0700
@@ -98,8 +98,8 @@
     @DataProvider(name = "knownClassFiles")
     private Object[][] knownClassFiles() {
         return new Object[][] {
-            { "/java.base/java/lang/Object.class" },
-            { "java.base/java/lang/Object.class" },
+            { "/modules/java.base/java/lang/Object.class" },
+            { "modules/java.base/java/lang/Object.class" },
         };
     }
 
@@ -126,14 +126,14 @@
             { "./"                    },
             { "/."                    },
             { "/./"                   },
-            { "/java.base/.."         },
-            { "/java.base/../"        },
-            { "/java.base/../."       },
-            { "/java.base"            },
-            { "/java.base/java/lang"  },
-            { "java.base/java/lang"   },
-            { "/java.base/java/lang/" },
-            { "java.base/java/lang/"  }
+            { "/modules/java.base/.."         },
+            { "/modules/java.base/../"        },
+            { "/modules/java.base/../."       },
+            { "/modules/java.base"            },
+            { "/modules/java.base/java/lang"  },
+            { "modules/java.base/java/lang"   },
+            { "/modules/java.base/java/lang/" },
+            { "modules/java.base/java/lang/"  }
         };
     }
 
@@ -208,23 +208,24 @@
     private Object[][] pathPrefixes() {
         return new Object[][] {
             { "/"                       },
-            { "java.base/java/lang"     },
-            { "./java.base/java/lang"   },
-            { "/java.base/java/lang"    },
-            { "/./java.base/java/lang"  },
-            { "java.base/java/lang/"    },
-            { "./java.base/java/lang/"  },
-            { "/./java.base/java/lang/" },
+            { "modules/java.base/java/lang"     },
+            { "./modules/java.base/java/lang"   },
+            { "/modules/java.base/java/lang"    },
+            { "/./modules/java.base/java/lang"  },
+            { "modules/java.base/java/lang/"    },
+            { "./modules/java.base/java/lang/"  },
+            { "/./modules/java.base/java/lang/" },
         };
     }
 
-    @Test(dataProvider = "pathPrefixes")
+    // @Test(dataProvider = "pathPrefixes")
     public void testParentInDirList(String dir) throws Exception {
         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
         Path base = fs.getPath(dir);
         try (DirectoryStream<Path> stream = Files.newDirectoryStream(base)) {
             for (Path entry: stream) {
-                assertTrue( entry.getParent().equals(base) );
+                assertTrue( entry.getParent().equals(base),
+                    base.toString() + "-> " + entry.toString() );
             }
         }
     }
@@ -232,10 +233,10 @@
     @DataProvider(name = "dirStreamStringFilterData")
     private Object[][] dirStreamStringFilterData() {
         return new Object[][] {
-            { "/java.base/java/lang", "/reflect"      },
-            { "/java.base/java/lang", "/Object.class" },
-            { "/java.base/java/util", "/stream"       },
-            { "/java.base/java/util", "/List.class"   },
+            { "/modules/java.base/java/lang", "/reflect"      },
+            { "/modules/java.base/java/lang", "/Object.class" },
+            { "/modules/java.base/java/util", "/stream"       },
+            { "/modules/java.base/java/util", "/List.class"   },
         };
     }
 
@@ -274,7 +275,7 @@
               "isDirectory"
             },
             {
-              "/java.base/java/lang",
+              "/modules/java.base/java/lang",
               (DirectoryStream.Filter<Path>)(Files::isRegularFile),
               "isFile"
             }
@@ -322,7 +323,7 @@
         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
         // This test assumes at least there are two elements in "java/lang"
         // package with any filter passed. don't change to different path here!
-        Path dir = fs.getPath("/java.base/java/lang");
+        Path dir = fs.getPath("/modules/java.base/java/lang");
         try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
             Iterator<Path> itr = stream.iterator();
             itr.hasNext();
@@ -379,9 +380,9 @@
             { "/META-INF" },
             { "/META-INF/services" },
             { "/META-INF/services/java.nio.file.spi.FileSystemProvider" },
-            { "/java.base/packages.offsets" },
-            { "/java.instrument/packages.offsets" },
-            { "/jdk.zipfs/packages.offsets" },
+            { "/modules/java.base/packages.offsets" },
+            { "/modules/java.instrument/packages.offsets" },
+            { "/modules/jdk.zipfs/packages.offsets" },
             { "/java/lang" },
             { "/java/util" },
         };
@@ -396,20 +397,20 @@
     @DataProvider(name = "pathGlobPatterns")
     private Object[][] pathGlobPatterns() {
         return new Object[][] {
-            { "/*", "/java.base", true },
-            { "/*", "/java.base/java", false },
-            { "/j*", "/java.base", true },
-            { "/J*", "/java.base", false },
-            { "**.class", "/java.base/java/lang/Object.class", true },
-            { "**.java", "/java.base/java/lang/Object.class", false },
-            { "**java/*", "/java.base/java/lang", true },
-            { "**java/lang/ref*", "/java.base/java/lang/reflect", true },
-            { "**java/lang/ref*", "/java.base/java/lang/ref", true },
-            { "**java/lang/ref?", "/java.base/java/lang/ref", false },
-            { "**java/lang/{ref,refl*}", "/java.base/java/lang/ref", true },
-            { "**java/lang/{ref,refl*}", "/java.base/java/lang/reflect", true },
-            { "**java/[a-u]?*/*.class", "/java.base/java/util/Map.class", true },
-            { "**java/util/[a-z]*.class", "/java.base/java/util/TreeMap.class", false },
+            { "/modules/*", "/modules/java.base", true },
+            { "/modules/*", "/modules/java.base/java", false },
+            { "/modules/j*", "/modules/java.base", true },
+            { "/modules/J*", "/modules/java.base", false },
+            { "**.class", "/modules/java.base/java/lang/Object.class", true },
+            { "**.java", "/modules/java.base/java/lang/Object.class", false },
+            { "**java/*", "/modules/java.base/java/lang", true },
+            { "**java/lang/ref*", "/modules/java.base/java/lang/reflect", true },
+            { "**java/lang/ref*", "/modules/java.base/java/lang/ref", true },
+            { "**java/lang/ref?", "/modules/java.base/java/lang/ref", false },
+            { "**java/lang/{ref,refl*}", "/modules/java.base/java/lang/ref", true },
+            { "**java/lang/{ref,refl*}", "/modules/java.base/java/lang/reflect", true },
+            { "**java/[a-u]?*/*.class", "/modules/java.base/java/util/Map.class", true },
+            { "**java/util/[a-z]*.class", "/modules/java.base/java/util/TreeMap.class", false },
         };
     }
 
@@ -428,20 +429,20 @@
     @DataProvider(name = "pathRegexPatterns")
     private Object[][] pathRegexPatterns() {
         return new Object[][] {
-            { "/.*", "/java.base", true },
-            { "/[^/]*", "/java.base/java", false },
-            { "/j.*", "/java.base", true },
-            { "/J.*", "/java.base", false },
-            { ".*\\.class", "/java.base/java/lang/Object.class", true },
-            { ".*\\.java", "/java.base/java/lang/Object.class", false },
-            { ".*java/.*", "/java.base/java/lang", true },
-            { ".*java/lang/ref.*", "/java.base/java/lang/reflect", true },
-            { ".*java/lang/ref.*", "/java.base/java/lang/ref", true },
-            { ".*/java/lang/ref.+", "/java.base/java/lang/ref", false },
-            { ".*/java/lang/(ref|refl.*)", "/java.base/java/lang/ref", true },
-            { ".*/java/lang/(ref|refl.*)", "/java.base/java/lang/reflect", true },
-            { ".*/java/[a-u]?.*/.*\\.class", "/java.base/java/util/Map.class", true },
-            { ".*/java/util/[a-z]*\\.class", "/java.base/java/util/TreeMap.class", false },
+            { "/modules/.*", "/modules/java.base", true },
+            { "/modules/[^/]*", "/modules/java.base/java", false },
+            { "/modules/j.*", "/modules/java.base", true },
+            { "/modules/J.*", "/modules/java.base", false },
+            { ".*\\.class", "/modules/java.base/java/lang/Object.class", true },
+            { ".*\\.java", "/modules/java.base/java/lang/Object.class", false },
+            { ".*java/.*", "/modules/java.base/java/lang", true },
+            { ".*java/lang/ref.*", "/modules/java.base/java/lang/reflect", true },
+            { ".*java/lang/ref.*", "/modules/java.base/java/lang/ref", true },
+            { ".*/java/lang/ref.+", "/modules/java.base/java/lang/ref", false },
+            { ".*/java/lang/(ref|refl.*)", "/modules/java.base/java/lang/ref", true },
+            { ".*/java/lang/(ref|refl.*)", "/modules/java.base/java/lang/reflect", true },
+            { ".*/java/[a-u]?.*/.*\\.class", "/modules/java.base/java/util/Map.class", true },
+            { ".*/java/util/[a-z]*\\.class", "/modules/java.base/java/util/TreeMap.class", false },
         };
     }
 
@@ -456,4 +457,159 @@
             p + (expectMatch? " should match " : " should not match ") +
             pattern);
     }
+
+    @Test
+    public void testPackagesAndModules() throws Exception {
+        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+        assertTrue(Files.isDirectory(fs.getPath("/packages")));
+        assertTrue(Files.isDirectory(fs.getPath("/modules")));
+    }
+
+    @DataProvider(name = "packagesSubDirs")
+    private Object[][] packagesSubDirs() {
+        return new Object[][] {
+            { "java.lang" },
+            { "java.util" },
+            { "java.nio"  },
+            { "jdk.nashorn.api.scripting" }
+        };
+    }
+
+    @Test(dataProvider = "packagesSubDirs")
+    public void testPackagesSubDirs(String pkg) throws Exception {
+        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+        assertTrue(Files.isDirectory(fs.getPath("/packages/" + pkg)),
+            pkg + " missing");
+    }
+
+    @DataProvider(name = "packagesLinks")
+    private Object[][] packagesLinks() {
+        return new Object[][] {
+            { "/packages/java.lang/java.base" },
+            { "/packages/java.lang/java.instrument" },
+            { "/packages/java/java.base" },
+            { "/packages/java/java.instrument" },
+            { "/packages/java/java.rmi"  },
+            { "/packages/java/java.sql"  },
+            { "/packages/javax/java.base"  },
+            { "/packages/javax/java.sql"  },
+            { "/packages/javax/java.xml"  },
+            { "/packages/javax/java.management"  },
+            { "/packages/java.util/java.base" },
+            { "/packages/jdk.nashorn.api.scripting/jdk.scripting.nashorn" },
+        };
+    }
+
+    @Test(dataProvider = "packagesLinks")
+    public void testPackagesLinks(String link) throws Exception {
+        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+        Path path = fs.getPath(link);
+        assertTrue(Files.exists(path), link + " missing");
+        assertTrue(Files.isSymbolicLink(path), path + " is not a link");
+        path = Files.readSymbolicLink(path);
+        assertEquals(path.toString(), "/modules" + link.substring(link.lastIndexOf("/")));
+    }
+
+    @DataProvider(name = "modulesSubDirs")
+    private Object[][] modulesSubDirs() {
+        return new Object[][] {
+            { "java.base" },
+            { "java.sql" },
+            { "jdk.scripting.nashorn" },
+        };
+    }
+
+    @Test(dataProvider = "modulesSubDirs")
+    public void testModulesSubDirs(String module) throws Exception {
+        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+        Path path = fs.getPath("/modules/" + module);
+        assertTrue(Files.isDirectory(path), module + " missing");
+        assertTrue(!Files.isSymbolicLink(path), path + " is a link");
+    }
+
+    @DataProvider(name="linkChases")
+    private Object[][] linkChases() {
+        return new Object[][] {
+            { "/modules/java.base/java/lang" },
+            { "/modules/java.base/java/util/Vector.class" },
+            { "/modules/jdk.scripting.nashorn/jdk/nashorn" },
+            { "/packages/java.lang/java.base/java/lang" },
+            { "/packages/java.util/java.base/java/util/Vector.class" },
+        };
+    }
+
+    @Test(dataProvider = "linkChases")
+    public void testLinkChases(String link) throws Exception {
+        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+        Path path = fs.getPath(link);
+        assertTrue(Files.exists(path), link);
+    }
+
+    @Test
+    public void testSymlinkDirList() throws Exception {
+        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+        Path path = fs.getPath("/packages/java.lang/java.base");
+        assertTrue(Files.isSymbolicLink(path));
+        assertTrue(Files.isDirectory(path));
+
+        boolean javaSeen = false, javaxSeen = false;
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
+            for (Path p : stream) {
+                String str = p.toString();
+                if (str.endsWith("/java")) {
+                    javaSeen = true;
+                } else if (str.endsWith("javax")) {
+                    javaxSeen = true;
+                }
+            }
+        }
+        assertTrue(javaSeen);
+        assertTrue(javaxSeen);
+    }
+
+    @Test
+    public void testPackagesSubDirList() throws Exception {
+        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+        String pathName = "/packages/javax.annotation";
+        Path path = fs.getPath(pathName);
+        boolean seenJavaCompiler = false, seenAnnotationsCommon = false;
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
+            for (Path p : stream) {
+               String str = p.toString();
+               if (str.equals(pathName + "/java.compiler")) {
+                   seenJavaCompiler = true;
+               } else if (str.equals(pathName + "/java.annotations.common")) {
+                   seenAnnotationsCommon = true;
+               }
+            }
+        }
+        assertTrue(seenJavaCompiler);
+        assertTrue(seenAnnotationsCommon);
+    }
+
+    @Test
+    public void testRootDirList() throws Exception {
+        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+        Path path = fs.getPath("/");
+        // check /packages and /modules are not repeated
+        // and seen once.
+        boolean packages = false, modules = false;
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
+            for (Path p : stream) {
+                String str = p.toString();
+                switch (str) {
+                    case "/packages":
+                        assertFalse(packages, "/packages repeated");
+                        packages = true;
+                        break;
+                    case "/modules":
+                        assertFalse(modules, "/modules repeated");
+                        modules = true;
+                        break;
+                }
+            }
+        }
+        assertTrue(packages, "/packages missing in / list!");
+        assertTrue(modules, "/modules missing in / list!");
+    }
 }
--- a/jdk/test/jdk/internal/jrtfs/WithSecurityManager.java	Wed Jul 05 20:41:30 2017 +0200
+++ b/jdk/test/jdk/internal/jrtfs/WithSecurityManager.java	Thu Jul 09 22:46:18 2015 -0700
@@ -55,7 +55,8 @@
             FileSystems.getFileSystem(URI.create("jrt:/"));
             if (!allow) throw new RuntimeException("access not expected");
         } catch (SecurityException se) {
-            if (allow) throw new RuntimeException("access expected");
+            if (allow)
+                throw se;
         }
 
         // check FileSystems.newFileSystem
@@ -63,7 +64,8 @@
             FileSystems.newFileSystem(URI.create("jrt:/"), null);
             if (!allow) throw new RuntimeException("access not expected");
         } catch (SecurityException se) {
-            if (allow) throw new RuntimeException("access expected");
+            if (allow)
+                throw se;
         }
 
         // check Paths.get
@@ -71,7 +73,8 @@
             Paths.get(URI.create("jrt:/java.base/java/lang/Object.class"));
             if (!allow) throw new RuntimeException("access not expected");
         } catch (SecurityException se) {
-            if (allow) throw new RuntimeException("access expected");
+            if (allow)
+                throw se;
         }
     }
 }