--- a/jdk/make/Tools.gmk Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/make/Tools.gmk Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/make/mapfiles/libjava/mapfile-vers Thu Jul 09 14:48:12 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/src/classes/build/tools/module/ImageBuilder.java Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/make/src/classes/build/tools/module/ImageBuilder.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/make/src/classes/build/tools/module/ModuleArchive.java Thu Jul 09 14:48:12 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/jdk/internal/jimage/Archive.java Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/Archive.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageWriter.java Thu Jul 09 14:48:12 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 14:48:12 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 14:48:12 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 Fri Jul 03 07:23:45 2015 +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 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageHeader.java Thu Jul 09 14:48:12 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 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java Thu Jul 09 14:48:12 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 14:48:12 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 14:48:12 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 14:48:12 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 14:48:12 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 Fri Jul 03 07:23:45 2015 +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 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java Thu Jul 09 14:48:12 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 14:48:12 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 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageStream.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageStrings.java Thu Jul 09 14:48:12 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 14:48:12 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 14:48:12 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 14:48:12 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 Fri Jul 03 07:23:45 2015 +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 Fri Jul 03 07:23:45 2015 +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 14:48:12 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 Fri Jul 03 07:23:45 2015 +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 14:48:12 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 14:48:12 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 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/UTF8String.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +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 14:48:12 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 14:48:12 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 14:48:12 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 14:48:12 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 14:48:12 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 14:48:12 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 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/SystemImages.java Thu Jul 09 14:48:12 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/native/include/jvm.h Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/java.base/share/native/include/jvm.h Thu Jul 09 14:48:12 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 14:48:12 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/unix/native/libjava/ConcurrentPReader_md.c Fri Jul 03 07:23:45 2015 +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 Fri Jul 03 07:23:45 2015 +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 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/jdk.dev/share/classes/jdk/tools/jimage/JImageTask.java Thu Jul 09 14:48:12 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 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/jdk.dev/share/classes/jdk/tools/jimage/resources/jimage.properties Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/src/jdk.rmic/share/classes/sun/tools/java/ClassPath.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/com/sun/jdi/cds/CDSBreakpointTest.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/com/sun/jdi/cds/CDSDeleteAllBkptsTest.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/com/sun/jdi/cds/CDSFieldWatchpoints.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/java/lang/Class/getDeclaredField/FieldSetAccessibleTest.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/java/nio/Buffer/LimitDirectMemory.sh Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/java/nio/charset/Charset/NIOCharsetAvailabilityTest.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/java/nio/file/spi/SetDefaultProvider.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/javax/management/monitor/GaugeMonitorDeadlockTest.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/javax/management/monitor/StringMonitorDeadlockTest.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/jdk/internal/jimage/VerifyJimage.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/jdk/internal/jrtfs/Basic.java Thu Jul 09 14:48:12 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 Fri Jul 03 07:23:45 2015 +0200
+++ b/jdk/test/jdk/internal/jrtfs/WithSecurityManager.java Thu Jul 09 14:48:12 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;
}
}
}