Merge
This commit is contained in:
commit
867e029083
@ -144,7 +144,6 @@ $(eval $(call SetupJavaCompilation,BUILD_INTERIM_JIMAGE, \
|
||||
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
|
||||
|
@ -239,6 +239,16 @@ SUNWprivate_1.1 {
|
||||
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 @@ SUNWprivate_1.1 {
|
||||
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
|
||||
|
@ -32,8 +32,8 @@ SUNWprivate_1.1 {
|
||||
Java_java_util_zip_Adler32_updateBytes;
|
||||
Java_java_util_zip_Adler32_updateByteBuffer;
|
||||
Java_java_util_zip_CRC32_update;
|
||||
Java_java_util_zip_CRC32_updateBytes;
|
||||
Java_java_util_zip_CRC32_updateByteBuffer;
|
||||
Java_java_util_zip_CRC32_updateBytes0;
|
||||
Java_java_util_zip_CRC32_updateByteBuffer0;
|
||||
Java_java_util_zip_Deflater_deflateBytes;
|
||||
Java_java_util_zip_Deflater_end;
|
||||
Java_java_util_zip_Deflater_getAdler;
|
||||
|
@ -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.IOException;
|
||||
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.Map;
|
||||
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 @@ class ImageBuilder {
|
||||
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 @@ class ImageBuilder {
|
||||
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 @@ class ImageBuilder {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
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 "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;
|
||||
}, "--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 "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 @@ class ImageBuilder {
|
||||
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) {
|
||||
|
@ -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 @@ public class ModuleArchive implements Archive {
|
||||
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 @@ public class ModuleArchive implements Archive {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private enum Section {
|
||||
CLASSES,
|
||||
CMDS,
|
||||
LIBS,
|
||||
CONFIGS
|
||||
public void open() throws IOException {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
@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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e != null) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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 static class FileEntry implements Entry {
|
||||
private final String name;
|
||||
private final InputStream is;
|
||||
private class FileEntry extends Entry {
|
||||
private final boolean isDirectory;
|
||||
private final Section section;
|
||||
FileEntry(String name, InputStream is,
|
||||
boolean isDirectory, Section section) {
|
||||
this.name = name;
|
||||
this.is = is;
|
||||
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.section = section;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public Section getSection() {
|
||||
return section;
|
||||
}
|
||||
public InputStream getInputStream() {
|
||||
return is;
|
||||
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 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,9 @@ package com.sun.crypto.provider;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* Rijndael --pronounced Reindaal-- is a symmetric cipher with a 128-bit
|
||||
@ -346,7 +349,16 @@ final class AESCrypt extends SymmetricCipher implements AESConstants
|
||||
* Encrypt exactly one block of plaintext.
|
||||
*/
|
||||
void encryptBlock(byte[] in, int inOffset,
|
||||
byte[] out, int outOffset)
|
||||
byte[] out, int outOffset) {
|
||||
cryptBlockCheck(in, inOffset);
|
||||
cryptBlockCheck(out, outOffset);
|
||||
implEncryptBlock(in, inOffset, out, outOffset);
|
||||
}
|
||||
|
||||
// Encryption operation. Possibly replaced with a compiler intrinsic.
|
||||
@HotSpotIntrinsicCandidate
|
||||
private void implEncryptBlock(byte[] in, int inOffset,
|
||||
byte[] out, int outOffset)
|
||||
{
|
||||
int keyOffset = 0;
|
||||
int t0 = ((in[inOffset++] ) << 24 |
|
||||
@ -412,12 +424,20 @@ final class AESCrypt extends SymmetricCipher implements AESConstants
|
||||
out[outOffset ] = (byte)(S[(t2 ) & 0xFF] ^ (tt ));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decrypt exactly one block of plaintext.
|
||||
*/
|
||||
void decryptBlock(byte[] in, int inOffset,
|
||||
byte[] out, int outOffset)
|
||||
byte[] out, int outOffset) {
|
||||
cryptBlockCheck(in, inOffset);
|
||||
cryptBlockCheck(out, outOffset);
|
||||
implDecryptBlock(in, inOffset, out, outOffset);
|
||||
}
|
||||
|
||||
// Decrypt operation. Possibly replaced with a compiler intrinsic.
|
||||
@HotSpotIntrinsicCandidate
|
||||
private void implDecryptBlock(byte[] in, int inOffset,
|
||||
byte[] out, int outOffset)
|
||||
{
|
||||
int keyOffset = 4;
|
||||
int t0 = ((in[inOffset++] ) << 24 |
|
||||
@ -572,6 +592,25 @@ final class AESCrypt extends SymmetricCipher implements AESConstants
|
||||
out[outOffset ] = (byte)(Si[(a0 ) & 0xFF] ^ (t1 ));
|
||||
}
|
||||
|
||||
// Used to perform all checks required by the Java semantics
|
||||
// (i.e., null checks and bounds checks) on the input parameters
|
||||
// to encryptBlock and to decryptBlock.
|
||||
// Normally, the Java Runtime performs these checks, however, as
|
||||
// encryptBlock and decryptBlock are possibly replaced with
|
||||
// compiler intrinsics, the JDK performs the required checks instead.
|
||||
// Does not check accesses to class-internal (private) arrays.
|
||||
private static void cryptBlockCheck(byte[] array, int offset) {
|
||||
Objects.requireNonNull(array);
|
||||
|
||||
if (offset < 0 || offset >= array.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(offset);
|
||||
}
|
||||
|
||||
int largestIndex = offset + AES_BLOCK_SIZE - 1;
|
||||
if (largestIndex < 0 || largestIndex >= array.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(largestIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a user-supplied key material into a session key.
|
||||
|
@ -27,6 +27,9 @@ package com.sun.crypto.provider;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.ProviderException;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
|
||||
/**
|
||||
@ -138,15 +141,22 @@ class CipherBlockChaining extends FeedbackCipher {
|
||||
* @return the length of the encrypted data
|
||||
*/
|
||||
int encrypt(byte[] plain, int plainOffset, int plainLen,
|
||||
byte[] cipher, int cipherOffset)
|
||||
byte[] cipher, int cipherOffset) {
|
||||
cryptBlockSizeCheck(plainLen);
|
||||
cryptNullAndBoundsCheck(plain, plainOffset, plainLen);
|
||||
cryptNullAndBoundsCheck(cipher, cipherOffset, plainLen);
|
||||
return implEncrypt(plain, plainOffset, plainLen,
|
||||
cipher, cipherOffset);
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
private int implEncrypt(byte[] plain, int plainOffset, int plainLen,
|
||||
byte[] cipher, int cipherOffset)
|
||||
{
|
||||
if ((plainLen % blockSize) != 0) {
|
||||
throw new ProviderException("Internal error in input buffering");
|
||||
}
|
||||
int endIndex = plainOffset + plainLen;
|
||||
|
||||
for (; plainOffset < endIndex;
|
||||
plainOffset+=blockSize, cipherOffset += blockSize) {
|
||||
plainOffset += blockSize, cipherOffset += blockSize) {
|
||||
for (int i = 0; i < blockSize; i++) {
|
||||
k[i] = (byte)(plain[i + plainOffset] ^ r[i]);
|
||||
}
|
||||
@ -179,11 +189,17 @@ class CipherBlockChaining extends FeedbackCipher {
|
||||
* @return the length of the decrypted data
|
||||
*/
|
||||
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
|
||||
byte[] plain, int plainOffset)
|
||||
byte[] plain, int plainOffset) {
|
||||
cryptBlockSizeCheck(cipherLen);
|
||||
cryptNullAndBoundsCheck(cipher, cipherOffset, cipherLen);
|
||||
cryptNullAndBoundsCheck(plain, plainOffset, cipherLen);
|
||||
return implDecrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
private int implDecrypt(byte[] cipher, int cipherOffset, int cipherLen,
|
||||
byte[] plain, int plainOffset)
|
||||
{
|
||||
if ((cipherLen % blockSize) != 0) {
|
||||
throw new ProviderException("Internal error in input buffering");
|
||||
}
|
||||
int endIndex = cipherOffset + cipherLen;
|
||||
|
||||
for (; cipherOffset < endIndex;
|
||||
@ -196,4 +212,27 @@ class CipherBlockChaining extends FeedbackCipher {
|
||||
}
|
||||
return cipherLen;
|
||||
}
|
||||
|
||||
private void cryptBlockSizeCheck(int len) {
|
||||
if ((len % blockSize) != 0) {
|
||||
throw new ProviderException("Internal error in input buffering");
|
||||
}
|
||||
}
|
||||
|
||||
private static void cryptNullAndBoundsCheck(byte[] array, int offset, int len) {
|
||||
if (len <= 0) {
|
||||
return; // not an error because cryptImpl/decryptImpl won't execute if len <= 0
|
||||
}
|
||||
|
||||
Objects.requireNonNull(array);
|
||||
|
||||
if (offset < 0 || offset >= array.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(offset);
|
||||
}
|
||||
|
||||
int endIndex = offset + len - 1;
|
||||
if (endIndex < 0 || endIndex >= array.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(endIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ package com.sun.crypto.provider;
|
||||
|
||||
import java.security.ProviderException;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* This class represents the GHASH function defined in NIST 800-38D
|
||||
* under section 6.4. It needs to be constructed w/ a hash subkey, i.e.
|
||||
@ -227,6 +229,7 @@ final class GHASH {
|
||||
* the hotspot signature. This method and methods called by it, cannot
|
||||
* throw exceptions or allocate arrays as it will breaking intrinsics
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
private static void processBlocks(byte[] data, int inOfs, int blocks, long[] st, long[] subH) {
|
||||
int offset = inOfs;
|
||||
while (blocks > 0) {
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package java.lang;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The Boolean class wraps a value of the primitive type
|
||||
* {@code boolean} in an object. An object of type
|
||||
@ -128,6 +130,7 @@ public final class Boolean implements java.io.Serializable,
|
||||
*
|
||||
* @return the primitive {@code boolean} value of this object.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public boolean booleanValue() {
|
||||
return value;
|
||||
}
|
||||
@ -146,6 +149,7 @@ public final class Boolean implements java.io.Serializable,
|
||||
* @return a {@code Boolean} instance representing {@code b}.
|
||||
* @since 1.4
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static Boolean valueOf(boolean b) {
|
||||
return (b ? TRUE : FALSE);
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package java.lang;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
*
|
||||
* The {@code Byte} class wraps a value of primitive type {@code byte}
|
||||
@ -98,6 +100,7 @@ public final class Byte extends Number implements Comparable<Byte> {
|
||||
* @return a {@code Byte} instance representing {@code b}.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static Byte valueOf(byte b) {
|
||||
final int offset = 128;
|
||||
return ByteCache.cache[(int)b + offset];
|
||||
@ -320,6 +323,7 @@ public final class Byte extends Number implements Comparable<Byte> {
|
||||
* Returns the value of this {@code Byte} as a
|
||||
* {@code byte}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public byte byteValue() {
|
||||
return value;
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The {@code Character} class wraps a value of the primitive
|
||||
* type {@code char} in an object. An object of type
|
||||
@ -4569,6 +4571,7 @@ class Character implements java.io.Serializable, Comparable<Character> {
|
||||
* @return a <tt>Character</tt> instance representing <tt>c</tt>.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static Character valueOf(char c) {
|
||||
if (c <= 127) { // must cache
|
||||
return CharacterCache.cache[(int)c];
|
||||
@ -4581,6 +4584,7 @@ class Character implements java.io.Serializable, Comparable<Character> {
|
||||
* @return the primitive {@code char} value represented by
|
||||
* this object.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public char charValue() {
|
||||
return value;
|
||||
}
|
||||
@ -7181,6 +7185,7 @@ class Character implements java.io.Serializable, Comparable<Character> {
|
||||
* the bytes in the specified <tt>char</tt> value.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static char reverseBytes(char ch) {
|
||||
return (char) (((ch & 0xFF00) >> 8) | (ch << 8));
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
import sun.misc.Unsafe;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import sun.reflect.CallerSensitive;
|
||||
import sun.reflect.ConstantPool;
|
||||
import sun.reflect.Reflection;
|
||||
@ -502,6 +503,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native boolean isInstance(Object obj);
|
||||
|
||||
|
||||
@ -529,6 +531,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* null.
|
||||
* @since 1.1
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native boolean isAssignableFrom(Class<?> cls);
|
||||
|
||||
|
||||
@ -539,6 +542,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* @return {@code true} if this object represents an interface;
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native boolean isInterface();
|
||||
|
||||
|
||||
@ -549,6 +553,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* {@code false} otherwise.
|
||||
* @since 1.1
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native boolean isArray();
|
||||
|
||||
|
||||
@ -580,6 +585,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* @see java.lang.Void#TYPE
|
||||
* @since 1.1
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native boolean isPrimitive();
|
||||
|
||||
/**
|
||||
@ -751,6 +757,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
*
|
||||
* @return the direct superclass of the class represented by this object
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native Class<? super T> getSuperclass();
|
||||
|
||||
|
||||
@ -984,6 +991,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* @see java.lang.reflect.Modifier
|
||||
* @since 1.1
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native int getModifiers();
|
||||
|
||||
|
||||
@ -3382,6 +3390,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* @since 1.5
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@HotSpotIntrinsicCandidate
|
||||
public T cast(Object obj) {
|
||||
if (obj != null && !isInstance(obj))
|
||||
throw new ClassCastException(cannotCastMsg(obj));
|
||||
|
@ -27,6 +27,7 @@ package java.lang;
|
||||
|
||||
import sun.misc.FloatingDecimal;
|
||||
import sun.misc.DoubleConsts;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The {@code Double} class wraps a value of the primitive type
|
||||
@ -514,6 +515,7 @@ public final class Double extends Number implements Comparable<Double> {
|
||||
* @return a {@code Double} instance representing {@code d}.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static Double valueOf(double d) {
|
||||
return new Double(d);
|
||||
}
|
||||
@ -711,6 +713,7 @@ public final class Double extends Number implements Comparable<Double> {
|
||||
*
|
||||
* @return the {@code double} value represented by this object
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public double doubleValue() {
|
||||
return value;
|
||||
}
|
||||
@ -831,6 +834,7 @@ public final class Double extends Number implements Comparable<Double> {
|
||||
* @param value a {@code double} precision floating-point number.
|
||||
* @return the bits that represent the floating-point number.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static long doubleToLongBits(double value) {
|
||||
if (!isNaN(value)) {
|
||||
return doubleToRawLongBits(value);
|
||||
@ -874,6 +878,7 @@ public final class Double extends Number implements Comparable<Double> {
|
||||
* @return the bits that represent the floating-point number.
|
||||
* @since 1.3
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native long doubleToRawLongBits(double value);
|
||||
|
||||
/**
|
||||
@ -937,6 +942,7 @@ public final class Double extends Number implements Comparable<Double> {
|
||||
* @return the {@code double} floating-point value with the same
|
||||
* bit pattern.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native double longBitsToDouble(long bits);
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,7 @@ package java.lang;
|
||||
import sun.misc.FloatingDecimal;
|
||||
import sun.misc.FloatConsts;
|
||||
import sun.misc.DoubleConsts;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The {@code Float} class wraps a value of primitive type
|
||||
@ -429,6 +430,7 @@ public final class Float extends Number implements Comparable<Float> {
|
||||
* @return a {@code Float} instance representing {@code f}.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static Float valueOf(float f) {
|
||||
return new Float(f);
|
||||
}
|
||||
@ -622,6 +624,7 @@ public final class Float extends Number implements Comparable<Float> {
|
||||
*
|
||||
* @return the {@code float} value represented by this object
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public float floatValue() {
|
||||
return value;
|
||||
}
|
||||
@ -740,6 +743,7 @@ public final class Float extends Number implements Comparable<Float> {
|
||||
* @param value a floating-point number.
|
||||
* @return the bits that represent the floating-point number.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int floatToIntBits(float value) {
|
||||
if (!isNaN(value)) {
|
||||
return floatToRawIntBits(value);
|
||||
@ -782,6 +786,7 @@ public final class Float extends Number implements Comparable<Float> {
|
||||
* @return the bits that represent the floating-point number.
|
||||
* @since 1.3
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native int floatToRawIntBits(float value);
|
||||
|
||||
/**
|
||||
@ -843,6 +848,7 @@ public final class Float extends Number implements Comparable<Float> {
|
||||
* @return the {@code float} floating-point value with the same bit
|
||||
* pattern.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native float intBitsToFloat(int bits);
|
||||
|
||||
/**
|
||||
|
@ -27,6 +27,7 @@ package java.lang;
|
||||
|
||||
import java.lang.annotation.Native;
|
||||
import java.util.Objects;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The {@code Integer} class wraps a value of the primitive type
|
||||
@ -395,6 +396,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
* @param i an integer to be converted.
|
||||
* @return a string representation of the argument in base 10.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static String toString(int i) {
|
||||
if (i == Integer.MIN_VALUE)
|
||||
return "-2147483648";
|
||||
@ -972,6 +974,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
* @return an {@code Integer} instance representing {@code i}.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static Integer valueOf(int i) {
|
||||
if (i >= IntegerCache.low && i <= IntegerCache.high)
|
||||
return IntegerCache.cache[i + (-IntegerCache.low)];
|
||||
@ -1035,6 +1038,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
* Returns the value of this {@code Integer} as an
|
||||
* {@code int}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public int intValue() {
|
||||
return value;
|
||||
}
|
||||
@ -1538,6 +1542,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
* is equal to zero.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int numberOfLeadingZeros(int i) {
|
||||
// HD, Figure 5-6
|
||||
if (i == 0)
|
||||
@ -1565,6 +1570,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
* to zero.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int numberOfTrailingZeros(int i) {
|
||||
// HD, Figure 5-14
|
||||
int y;
|
||||
@ -1587,6 +1593,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
* representation of the specified {@code int} value.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int bitCount(int i) {
|
||||
// HD, Figure 5-2
|
||||
i = i - ((i >>> 1) & 0x55555555);
|
||||
@ -1688,6 +1695,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
* {@code int} value.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int reverseBytes(int i) {
|
||||
return ((i >>> 24) ) |
|
||||
((i >> 8) & 0xFF00) |
|
||||
|
@ -28,6 +28,7 @@ package java.lang;
|
||||
import java.lang.annotation.Native;
|
||||
import java.math.*;
|
||||
import java.util.Objects;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
|
||||
/**
|
||||
@ -1074,6 +1075,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
* @return a {@code Long} instance representing {@code l}.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static Long valueOf(long l) {
|
||||
final int offset = 128;
|
||||
if (l >= -128 && l <= 127) { // will cache
|
||||
@ -1238,6 +1240,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
* Returns the value of this {@code Long} as a
|
||||
* {@code long} value.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public long longValue() {
|
||||
return value;
|
||||
}
|
||||
@ -1655,6 +1658,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
* is equal to zero.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int numberOfLeadingZeros(long i) {
|
||||
// HD, Figure 5-6
|
||||
if (i == 0)
|
||||
@ -1684,6 +1688,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
* to zero.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int numberOfTrailingZeros(long i) {
|
||||
// HD, Figure 5-14
|
||||
int x, y;
|
||||
@ -1707,6 +1712,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
* representation of the specified {@code long} value.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int bitCount(long i) {
|
||||
// HD, Figure 5-2
|
||||
i = i - ((i >>> 1) & 0x5555555555555555L);
|
||||
@ -1810,6 +1816,7 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
* {@code long} value.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static long reverseBytes(long i) {
|
||||
i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;
|
||||
return (i << 48) | ((i & 0xffff0000L) << 16) |
|
||||
|
@ -24,10 +24,11 @@
|
||||
*/
|
||||
|
||||
package java.lang;
|
||||
import java.util.Random;
|
||||
|
||||
import java.util.Random;
|
||||
import sun.misc.FloatConsts;
|
||||
import sun.misc.DoubleConsts;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The class {@code Math} contains methods for performing basic
|
||||
@ -147,6 +148,7 @@ public final class Math {
|
||||
* @param a an angle, in radians.
|
||||
* @return the sine of the argument.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static double sin(double a) {
|
||||
return StrictMath.sin(a); // default impl. delegates to StrictMath
|
||||
}
|
||||
@ -162,6 +164,7 @@ public final class Math {
|
||||
* @param a an angle, in radians.
|
||||
* @return the cosine of the argument.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static double cos(double a) {
|
||||
return StrictMath.cos(a); // default impl. delegates to StrictMath
|
||||
}
|
||||
@ -179,6 +182,7 @@ public final class Math {
|
||||
* @param a an angle, in radians.
|
||||
* @return the tangent of the argument.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static double tan(double a) {
|
||||
return StrictMath.tan(a); // default impl. delegates to StrictMath
|
||||
}
|
||||
@ -280,6 +284,7 @@ public final class Math {
|
||||
* @return the value <i>e</i><sup>{@code a}</sup>,
|
||||
* where <i>e</i> is the base of the natural logarithms.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static double exp(double a) {
|
||||
return StrictMath.exp(a); // default impl. delegates to StrictMath
|
||||
}
|
||||
@ -301,6 +306,7 @@ public final class Math {
|
||||
* @return the value ln {@code a}, the natural logarithm of
|
||||
* {@code a}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static double log(double a) {
|
||||
return StrictMath.log(a); // default impl. delegates to StrictMath
|
||||
}
|
||||
@ -326,6 +332,7 @@ public final class Math {
|
||||
* @return the base 10 logarithm of {@code a}.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static double log10(double a) {
|
||||
return StrictMath.log10(a); // default impl. delegates to StrictMath
|
||||
}
|
||||
@ -347,6 +354,7 @@ public final class Math {
|
||||
* @return the positive square root of {@code a}.
|
||||
* If the argument is NaN or less than zero, the result is NaN.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static double sqrt(double a) {
|
||||
return StrictMath.sqrt(a); // default impl. delegates to StrictMath
|
||||
// Note that hardware sqrt instructions
|
||||
@ -525,6 +533,7 @@ public final class Math {
|
||||
* in polar coordinates that corresponds to the point
|
||||
* (<i>x</i>, <i>y</i>) in Cartesian coordinates.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static double atan2(double y, double x) {
|
||||
return StrictMath.atan2(y, x); // default impl. delegates to StrictMath
|
||||
}
|
||||
@ -652,6 +661,7 @@ public final class Math {
|
||||
* @param b the exponent.
|
||||
* @return the value {@code a}<sup>{@code b}</sup>.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static double pow(double a, double b) {
|
||||
return StrictMath.pow(a, b); // default impl. delegates to StrictMath
|
||||
}
|
||||
@ -806,6 +816,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows an int
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int addExact(int x, int y) {
|
||||
int r = x + y;
|
||||
// HD 2-12 Overflow iff both arguments have the opposite sign of the result
|
||||
@ -825,6 +836,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows a long
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static long addExact(long x, long y) {
|
||||
long r = x + y;
|
||||
// HD 2-12 Overflow iff both arguments have the opposite sign of the result
|
||||
@ -844,6 +856,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows an int
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int subtractExact(int x, int y) {
|
||||
int r = x - y;
|
||||
// HD 2-12 Overflow iff the arguments have different signs and
|
||||
@ -864,6 +877,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows a long
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static long subtractExact(long x, long y) {
|
||||
long r = x - y;
|
||||
// HD 2-12 Overflow iff the arguments have different signs and
|
||||
@ -884,6 +898,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows an int
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int multiplyExact(int x, int y) {
|
||||
long r = (long)x * (long)y;
|
||||
if ((int)r != r) {
|
||||
@ -902,6 +917,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows a long
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static long multiplyExact(long x, long y) {
|
||||
long r = x * y;
|
||||
long ax = Math.abs(x);
|
||||
@ -927,6 +943,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows an int
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int incrementExact(int a) {
|
||||
if (a == Integer.MAX_VALUE) {
|
||||
throw new ArithmeticException("integer overflow");
|
||||
@ -944,6 +961,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows a long
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static long incrementExact(long a) {
|
||||
if (a == Long.MAX_VALUE) {
|
||||
throw new ArithmeticException("long overflow");
|
||||
@ -961,6 +979,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows an int
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int decrementExact(int a) {
|
||||
if (a == Integer.MIN_VALUE) {
|
||||
throw new ArithmeticException("integer overflow");
|
||||
@ -978,6 +997,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows a long
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static long decrementExact(long a) {
|
||||
if (a == Long.MIN_VALUE) {
|
||||
throw new ArithmeticException("long overflow");
|
||||
@ -995,6 +1015,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows an int
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int negateExact(int a) {
|
||||
if (a == Integer.MIN_VALUE) {
|
||||
throw new ArithmeticException("integer overflow");
|
||||
@ -1012,6 +1033,7 @@ public final class Math {
|
||||
* @throws ArithmeticException if the result overflows a long
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static long negateExact(long a) {
|
||||
if (a == Long.MIN_VALUE) {
|
||||
throw new ArithmeticException("long overflow");
|
||||
@ -1256,6 +1278,7 @@ public final class Math {
|
||||
* @param a the argument whose absolute value is to be determined
|
||||
* @return the absolute value of the argument.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static double abs(double a) {
|
||||
return (a <= 0.0D) ? 0.0D - a : a;
|
||||
}
|
||||
@ -1270,6 +1293,7 @@ public final class Math {
|
||||
* @param b another argument.
|
||||
* @return the larger of {@code a} and {@code b}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int max(int a, int b) {
|
||||
return (a >= b) ? a : b;
|
||||
}
|
||||
@ -1354,6 +1378,7 @@ public final class Math {
|
||||
* @param b another argument.
|
||||
* @return the smaller of {@code a} and {@code b}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int min(int a, int b) {
|
||||
return (a <= b) ? a : b;
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package java.lang;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* Class {@code Object} is the root of the class hierarchy.
|
||||
* Every class has {@code Object} as a superclass. All objects,
|
||||
@ -44,6 +46,7 @@ public class Object {
|
||||
/**
|
||||
* Constructs a new object.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public Object() {}
|
||||
|
||||
/**
|
||||
@ -65,6 +68,7 @@ public class Object {
|
||||
* class of this object.
|
||||
* @jls 15.8.2 Class Literals
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final native Class<?> getClass();
|
||||
|
||||
/**
|
||||
@ -101,6 +105,7 @@ public class Object {
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
* @see java.lang.System#identityHashCode
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native int hashCode();
|
||||
|
||||
/**
|
||||
@ -213,6 +218,7 @@ public class Object {
|
||||
* be cloned.
|
||||
* @see java.lang.Cloneable
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
protected native Object clone() throws CloneNotSupportedException;
|
||||
|
||||
/**
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package java.lang;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The {@code Short} class wraps a value of primitive type {@code
|
||||
* short} in an object. An object of type {@code Short} contains a
|
||||
@ -227,6 +229,7 @@ public final class Short extends Number implements Comparable<Short> {
|
||||
* @return a {@code Short} instance representing {@code s}.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static Short valueOf(short s) {
|
||||
final int offset = 128;
|
||||
int sAsInt = s;
|
||||
@ -334,6 +337,7 @@ public final class Short extends Number implements Comparable<Short> {
|
||||
* Returns the value of this {@code Short} as a
|
||||
* {@code short}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public short shortValue() {
|
||||
return value;
|
||||
}
|
||||
@ -487,6 +491,7 @@ public final class Short extends Number implements Comparable<Short> {
|
||||
* the bytes in the specified {@code short} value.
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static short reverseBytes(short i) {
|
||||
return (short) (((i & 0xFF00) >> 8) | (i << 8));
|
||||
}
|
||||
|
@ -24,8 +24,10 @@
|
||||
*/
|
||||
|
||||
package java.lang;
|
||||
|
||||
import java.util.Random;
|
||||
import sun.misc.DoubleConsts;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The class {@code StrictMath} contains methods for performing basic
|
||||
@ -243,7 +245,6 @@ public final class StrictMath {
|
||||
*/
|
||||
public static native double log(double a);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base 10 logarithm of a {@code double} value.
|
||||
* Special cases:
|
||||
@ -280,6 +281,7 @@ public final class StrictMath {
|
||||
* @param a a value.
|
||||
* @return the positive square root of {@code a}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native double sqrt(double a);
|
||||
|
||||
/**
|
||||
@ -521,7 +523,6 @@ public final class StrictMath {
|
||||
*/
|
||||
public static native double atan2(double y, double x);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value of the first argument raised to the power of the
|
||||
* second argument. Special cases:
|
||||
@ -1009,6 +1010,7 @@ public final class StrictMath {
|
||||
* @param b another argument.
|
||||
* @return the larger of {@code a} and {@code b}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int max(int a, int b) {
|
||||
return Math.max(a, b);
|
||||
}
|
||||
@ -1073,6 +1075,7 @@ public final class StrictMath {
|
||||
* @param b another argument.
|
||||
* @return the smaller of {@code a} and {@code b}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int min(int a, int b) {
|
||||
return Math.min(a, b);
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The {@code String} class represents character strings. All
|
||||
@ -152,6 +153,7 @@ public final class String
|
||||
* @param original
|
||||
* A {@code String}
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public String(String original) {
|
||||
this.value = original.value;
|
||||
this.hash = original.hash;
|
||||
@ -978,6 +980,7 @@ public final class String
|
||||
* @see #compareTo(String)
|
||||
* @see #equalsIgnoreCase(String)
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public boolean equals(Object anObject) {
|
||||
if (this == anObject) {
|
||||
return true;
|
||||
@ -1154,6 +1157,7 @@ public final class String
|
||||
* value greater than {@code 0} if this string is
|
||||
* lexicographically greater than the string argument.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public int compareTo(String anotherString) {
|
||||
char[] v1 = value;
|
||||
char[] v2 = anotherString.value;
|
||||
@ -1696,6 +1700,7 @@ public final class String
|
||||
* @return the index of the first occurrence of the specified substring,
|
||||
* or {@code -1} if there is no such occurrence.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public int indexOf(String str) {
|
||||
return indexOf(str, 0);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
package java.lang;
|
||||
|
||||
import java.util.Arrays;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* A thread-safe, mutable sequence of characters.
|
||||
@ -112,6 +113,7 @@ import java.util.Arrays;
|
||||
* Constructs a string buffer with no characters in it and an
|
||||
* initial capacity of 16 characters.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public StringBuffer() {
|
||||
super(16);
|
||||
}
|
||||
@ -124,6 +126,7 @@ import java.util.Arrays;
|
||||
* @exception NegativeArraySizeException if the {@code capacity}
|
||||
* argument is less than {@code 0}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public StringBuffer(int capacity) {
|
||||
super(capacity);
|
||||
}
|
||||
@ -135,6 +138,7 @@ import java.util.Arrays;
|
||||
*
|
||||
* @param str the initial contents of the buffer.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public StringBuffer(String str) {
|
||||
super(str.length() + 16);
|
||||
append(str);
|
||||
@ -271,6 +275,7 @@ import java.util.Arrays;
|
||||
}
|
||||
|
||||
@Override
|
||||
@HotSpotIntrinsicCandidate
|
||||
public synchronized StringBuffer append(String str) {
|
||||
toStringCache = null;
|
||||
super.append(str);
|
||||
@ -382,6 +387,7 @@ import java.util.Arrays;
|
||||
}
|
||||
|
||||
@Override
|
||||
@HotSpotIntrinsicCandidate
|
||||
public synchronized StringBuffer append(char c) {
|
||||
toStringCache = null;
|
||||
super.append(c);
|
||||
@ -389,6 +395,7 @@ import java.util.Arrays;
|
||||
}
|
||||
|
||||
@Override
|
||||
@HotSpotIntrinsicCandidate
|
||||
public synchronized StringBuffer append(int i) {
|
||||
toStringCache = null;
|
||||
super.append(i);
|
||||
@ -670,6 +677,7 @@ import java.util.Arrays;
|
||||
}
|
||||
|
||||
@Override
|
||||
@HotSpotIntrinsicCandidate
|
||||
public synchronized String toString() {
|
||||
if (toStringCache == null) {
|
||||
toStringCache = Arrays.copyOfRange(value, 0, count);
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package java.lang;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* A mutable sequence of characters. This class provides an API compatible
|
||||
@ -85,6 +86,7 @@ public final class StringBuilder
|
||||
* Constructs a string builder with no characters in it and an
|
||||
* initial capacity of 16 characters.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public StringBuilder() {
|
||||
super(16);
|
||||
}
|
||||
@ -97,6 +99,7 @@ public final class StringBuilder
|
||||
* @throws NegativeArraySizeException if the {@code capacity}
|
||||
* argument is less than {@code 0}.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public StringBuilder(int capacity) {
|
||||
super(capacity);
|
||||
}
|
||||
@ -108,6 +111,7 @@ public final class StringBuilder
|
||||
*
|
||||
* @param str the initial contents of the buffer.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public StringBuilder(String str) {
|
||||
super(str.length() + 16);
|
||||
append(str);
|
||||
@ -132,6 +136,7 @@ public final class StringBuilder
|
||||
}
|
||||
|
||||
@Override
|
||||
@HotSpotIntrinsicCandidate
|
||||
public StringBuilder append(String str) {
|
||||
super.append(str);
|
||||
return this;
|
||||
@ -198,12 +203,14 @@ public final class StringBuilder
|
||||
}
|
||||
|
||||
@Override
|
||||
@HotSpotIntrinsicCandidate
|
||||
public StringBuilder append(char c) {
|
||||
super.append(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@HotSpotIntrinsicCandidate
|
||||
public StringBuilder append(int i) {
|
||||
super.append(i);
|
||||
return this;
|
||||
@ -402,6 +409,7 @@ public final class StringBuilder
|
||||
}
|
||||
|
||||
@Override
|
||||
@HotSpotIntrinsicCandidate
|
||||
public String toString() {
|
||||
// Create a copy, don't share the array
|
||||
return new String(value, 0, count);
|
||||
|
@ -42,6 +42,7 @@ import sun.reflect.CallerSensitive;
|
||||
import sun.reflect.Reflection;
|
||||
import sun.security.util.SecurityConstants;
|
||||
import sun.reflect.annotation.AnnotationType;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The <code>System</code> class contains several useful class fields
|
||||
@ -349,6 +350,7 @@ public final class System {
|
||||
* the current time and midnight, January 1, 1970 UTC.
|
||||
* @see java.util.Date
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native long currentTimeMillis();
|
||||
|
||||
/**
|
||||
@ -392,6 +394,7 @@ public final class System {
|
||||
* high-resolution time source, in nanoseconds
|
||||
* @since 1.5
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native long nanoTime();
|
||||
|
||||
/**
|
||||
@ -486,6 +489,7 @@ public final class System {
|
||||
* @exception NullPointerException if either <code>src</code> or
|
||||
* <code>dest</code> is <code>null</code>.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native void arraycopy(Object src, int srcPos,
|
||||
Object dest, int destPos,
|
||||
int length);
|
||||
@ -501,6 +505,7 @@ public final class System {
|
||||
* @return the hashCode
|
||||
* @since 1.1
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native int identityHashCode(Object x);
|
||||
|
||||
/**
|
||||
|
@ -40,7 +40,7 @@ import sun.nio.ch.Interruptible;
|
||||
import sun.reflect.CallerSensitive;
|
||||
import sun.reflect.Reflection;
|
||||
import sun.security.util.SecurityConstants;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* A <i>thread</i> is a thread of execution in a program. The Java
|
||||
@ -261,6 +261,7 @@ class Thread implements Runnable {
|
||||
*
|
||||
* @return the currently executing thread.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native Thread currentThread();
|
||||
|
||||
/**
|
||||
@ -966,6 +967,7 @@ class Thread implements Runnable {
|
||||
* is reset or not based on the value of ClearInterrupted that is
|
||||
* passed.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
private native boolean isInterrupted(boolean ClearInterrupted);
|
||||
|
||||
/**
|
||||
|
@ -27,6 +27,7 @@ package java.lang.invoke;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
@ -476,6 +477,7 @@ public abstract class MethodHandle {
|
||||
* @throws WrongMethodTypeException if the target's type is not identical with the caller's symbolic type descriptor
|
||||
* @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
|
||||
|
||||
/**
|
||||
@ -513,6 +515,7 @@ public abstract class MethodHandle {
|
||||
* @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails
|
||||
* @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
|
||||
|
||||
/**
|
||||
@ -532,6 +535,7 @@ public abstract class MethodHandle {
|
||||
* @param args the signature-polymorphic parameter list, statically represented using varargs
|
||||
* @return the signature-polymorphic result, statically represented using {@code Object}
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
/*non-public*/ final native @PolymorphicSignature Object invokeBasic(Object... args) throws Throwable;
|
||||
|
||||
/**
|
||||
@ -541,6 +545,7 @@ public abstract class MethodHandle {
|
||||
* @param args the signature-polymorphic parameter list, statically represented using varargs
|
||||
* @return the signature-polymorphic result, statically represented using {@code Object}
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
/*non-public*/ static native @PolymorphicSignature Object linkToVirtual(Object... args) throws Throwable;
|
||||
|
||||
/**
|
||||
@ -550,6 +555,7 @@ public abstract class MethodHandle {
|
||||
* @param args the signature-polymorphic parameter list, statically represented using varargs
|
||||
* @return the signature-polymorphic result, statically represented using {@code Object}
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
/*non-public*/ static native @PolymorphicSignature Object linkToStatic(Object... args) throws Throwable;
|
||||
|
||||
/**
|
||||
@ -559,6 +565,7 @@ public abstract class MethodHandle {
|
||||
* @param args the signature-polymorphic parameter list, statically represented using varargs
|
||||
* @return the signature-polymorphic result, statically represented using {@code Object}
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
/*non-public*/ static native @PolymorphicSignature Object linkToSpecial(Object... args) throws Throwable;
|
||||
|
||||
/**
|
||||
@ -568,6 +575,7 @@ public abstract class MethodHandle {
|
||||
* @param args the signature-polymorphic parameter list, statically represented using varargs
|
||||
* @return the signature-polymorphic result, statically represented using {@code Object}
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
/*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args) throws Throwable;
|
||||
|
||||
/**
|
||||
|
@ -36,6 +36,7 @@ import sun.invoke.empty.Empty;
|
||||
import sun.invoke.util.ValueConversions;
|
||||
import sun.invoke.util.VerifyType;
|
||||
import sun.invoke.util.Wrapper;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import sun.reflect.CallerSensitive;
|
||||
import sun.reflect.Reflection;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
@ -709,6 +710,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
// Intrinsified by C2. Counters are used during parsing to calculate branch frequencies.
|
||||
@LambdaForm.Hidden
|
||||
@jdk.internal.HotSpotIntrinsicCandidate
|
||||
static
|
||||
boolean profileBoolean(boolean result, int[] counters) {
|
||||
// Profile is int[2] where [0] and [1] correspond to false and true occurrences respectively.
|
||||
@ -724,6 +726,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
// Intrinsified by C2. Returns true if obj is a compile-time constant.
|
||||
@LambdaForm.Hidden
|
||||
@jdk.internal.HotSpotIntrinsicCandidate
|
||||
static
|
||||
boolean isCompileConstant(Object obj) {
|
||||
return false;
|
||||
|
@ -29,6 +29,7 @@ import sun.misc.Cleaner;
|
||||
import sun.misc.JavaLangRefAccess;
|
||||
import sun.misc.ManagedLocalsThread;
|
||||
import sun.misc.SharedSecrets;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* Abstract base class for reference objects. This class defines the
|
||||
@ -251,6 +252,7 @@ public abstract class Reference<T> {
|
||||
* @return The object to which this reference refers, or
|
||||
* <code>null</code> if this reference object has been cleared
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public T get() {
|
||||
return this.referent;
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package java.lang.reflect;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* The {@code Array} class provides static methods to dynamically create and
|
||||
* access Java arrays.
|
||||
@ -119,6 +121,7 @@ class Array {
|
||||
* @exception IllegalArgumentException if the object argument is not
|
||||
* an array
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native int getLength(Object array)
|
||||
throws IllegalArgumentException;
|
||||
|
||||
@ -477,6 +480,7 @@ class Array {
|
||||
* Private
|
||||
*/
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
private static native Object newArray(Class<?> componentType, int length)
|
||||
throws NegativeArraySizeException;
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package java.lang.reflect;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import sun.reflect.CallerSensitive;
|
||||
import sun.reflect.MethodAccessor;
|
||||
import sun.reflect.Reflection;
|
||||
@ -485,6 +486,7 @@ public final class Method extends Executable {
|
||||
* provoked by this method fails.
|
||||
*/
|
||||
@CallerSensitive
|
||||
@HotSpotIntrinsicCandidate
|
||||
public Object invoke(Object obj, Object... args)
|
||||
throws IllegalAccessException, IllegalArgumentException,
|
||||
InvocationTargetException
|
||||
|
@ -34,10 +34,13 @@ import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamField;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import sun.misc.DoubleConsts;
|
||||
import sun.misc.FloatConsts;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* Immutable arbitrary-precision integers. All operations behave as if
|
||||
@ -262,6 +265,15 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
*/
|
||||
private static final int MULTIPLY_SQUARE_THRESHOLD = 20;
|
||||
|
||||
/**
|
||||
* The threshold for using an intrinsic version of
|
||||
* implMontgomeryXXX to perform Montgomery multiplication. If the
|
||||
* number of ints in the number is more than this value we do not
|
||||
* use the intrinsic.
|
||||
*/
|
||||
private static final int MONTGOMERY_INTRINSIC_THRESHOLD = 512;
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
/**
|
||||
@ -1639,7 +1651,14 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
* Multiplies int arrays x and y to the specified lengths and places
|
||||
* the result into z. There will be no leading zeros in the resultant array.
|
||||
*/
|
||||
private int[] multiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z) {
|
||||
private static int[] multiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z) {
|
||||
multiplyToLenCheck(x, xlen);
|
||||
multiplyToLenCheck(y, ylen);
|
||||
return implMultiplyToLen(x, xlen, y, ylen, z);
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
private static int[] implMultiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z) {
|
||||
int xstart = xlen - 1;
|
||||
int ystart = ylen - 1;
|
||||
|
||||
@ -1669,6 +1688,18 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
return z;
|
||||
}
|
||||
|
||||
private static void multiplyToLenCheck(int[] array, int length) {
|
||||
if (length <= 0) {
|
||||
return; // not an error because multiplyToLen won't execute if len <= 0
|
||||
}
|
||||
|
||||
Objects.requireNonNull(array);
|
||||
|
||||
if (length > array.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies two BigIntegers using the Karatsuba multiplication
|
||||
* algorithm. This is a recursive divide-and-conquer algorithm which is
|
||||
@ -1999,6 +2030,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
/**
|
||||
* Java Runtime may use intrinsic for this method.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
private static final int[] implSquareToLen(int[] x, int len, int[] z, int zlen) {
|
||||
/*
|
||||
* The algorithm used here is adapted from Colin Plumb's C library.
|
||||
@ -2601,6 +2633,77 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
return (invertResult ? result.modInverse(m) : result);
|
||||
}
|
||||
|
||||
// Montgomery multiplication. These are wrappers for
|
||||
// implMontgomeryXX routines which are expected to be replaced by
|
||||
// virtual machine intrinsics. We don't use the intrinsics for
|
||||
// very large operands: MONTGOMERY_INTRINSIC_THRESHOLD should be
|
||||
// larger than any reasonable crypto key.
|
||||
private static int[] montgomeryMultiply(int[] a, int[] b, int[] n, int len, long inv,
|
||||
int[] product) {
|
||||
implMontgomeryMultiplyChecks(a, b, n, len, product);
|
||||
if (len > MONTGOMERY_INTRINSIC_THRESHOLD) {
|
||||
// Very long argument: do not use an intrinsic
|
||||
product = multiplyToLen(a, len, b, len, product);
|
||||
return montReduce(product, n, len, (int)inv);
|
||||
} else {
|
||||
return implMontgomeryMultiply(a, b, n, len, inv, materialize(product, len));
|
||||
}
|
||||
}
|
||||
private static int[] montgomerySquare(int[] a, int[] n, int len, long inv,
|
||||
int[] product) {
|
||||
implMontgomeryMultiplyChecks(a, a, n, len, product);
|
||||
if (len > MONTGOMERY_INTRINSIC_THRESHOLD) {
|
||||
// Very long argument: do not use an intrinsic
|
||||
product = squareToLen(a, len, product);
|
||||
return montReduce(product, n, len, (int)inv);
|
||||
} else {
|
||||
return implMontgomerySquare(a, n, len, inv, materialize(product, len));
|
||||
}
|
||||
}
|
||||
|
||||
// Range-check everything.
|
||||
private static void implMontgomeryMultiplyChecks
|
||||
(int[] a, int[] b, int[] n, int len, int[] product) throws RuntimeException {
|
||||
if (len % 2 != 0) {
|
||||
throw new IllegalArgumentException("input array length must be even: " + len);
|
||||
}
|
||||
|
||||
if (len < 1) {
|
||||
throw new IllegalArgumentException("invalid input length: " + len);
|
||||
}
|
||||
|
||||
if (len > a.length ||
|
||||
len > b.length ||
|
||||
len > n.length ||
|
||||
(product != null && len > product.length)) {
|
||||
throw new IllegalArgumentException("input array length out of bound: " + len);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that the int array z (which is expected to contain
|
||||
// the result of a Montgomery multiplication) is present and
|
||||
// sufficiently large.
|
||||
private static int[] materialize(int[] z, int len) {
|
||||
if (z == null || z.length < len)
|
||||
z = new int[len];
|
||||
return z;
|
||||
}
|
||||
|
||||
// These methods are intended to be be replaced by virtual machine
|
||||
// intrinsics.
|
||||
@HotSpotIntrinsicCandidate
|
||||
private static int[] implMontgomeryMultiply(int[] a, int[] b, int[] n, int len,
|
||||
long inv, int[] product) {
|
||||
product = multiplyToLen(a, len, b, len, product);
|
||||
return montReduce(product, n, len, (int)inv);
|
||||
}
|
||||
@HotSpotIntrinsicCandidate
|
||||
private static int[] implMontgomerySquare(int[] a, int[] n, int len,
|
||||
long inv, int[] product) {
|
||||
product = squareToLen(a, len, product);
|
||||
return montReduce(product, n, len, (int)inv);
|
||||
}
|
||||
|
||||
static int[] bnExpModThreshTable = {7, 25, 81, 241, 673, 1793,
|
||||
Integer.MAX_VALUE}; // Sentinel
|
||||
|
||||
@ -2679,6 +2782,17 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
int[] mod = z.mag;
|
||||
int modLen = mod.length;
|
||||
|
||||
// Make modLen even. It is conventional to use a cryptographic
|
||||
// modulus that is 512, 768, 1024, or 2048 bits, so this code
|
||||
// will not normally be executed. However, it is necessary for
|
||||
// the correct functioning of the HotSpot intrinsics.
|
||||
if ((modLen & 1) != 0) {
|
||||
int[] x = new int[modLen + 1];
|
||||
System.arraycopy(mod, 0, x, 1, modLen);
|
||||
mod = x;
|
||||
modLen++;
|
||||
}
|
||||
|
||||
// Select an appropriate window size
|
||||
int wbits = 0;
|
||||
int ebits = bitLength(exp, exp.length);
|
||||
@ -2697,8 +2811,10 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
for (int i=0; i < tblmask; i++)
|
||||
table[i] = new int[modLen];
|
||||
|
||||
// Compute the modular inverse
|
||||
int inv = -MutableBigInteger.inverseMod32(mod[modLen-1]);
|
||||
// Compute the modular inverse of the least significant 64-bit
|
||||
// digit of the modulus
|
||||
long n0 = (mod[modLen-1] & LONG_MASK) + ((mod[modLen-2] & LONG_MASK) << 32);
|
||||
long inv = -MutableBigInteger.inverseMod64(n0);
|
||||
|
||||
// Convert base to Montgomery form
|
||||
int[] a = leftShift(base, base.length, modLen << 5);
|
||||
@ -2706,6 +2822,8 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
MutableBigInteger q = new MutableBigInteger(),
|
||||
a2 = new MutableBigInteger(a),
|
||||
b2 = new MutableBigInteger(mod);
|
||||
b2.normalize(); // MutableBigInteger.divide() assumes that its
|
||||
// divisor is in normal form.
|
||||
|
||||
MutableBigInteger r= a2.divide(b2, q);
|
||||
table[0] = r.toIntArray();
|
||||
@ -2714,22 +2832,19 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
if (table[0].length < modLen) {
|
||||
int offset = modLen - table[0].length;
|
||||
int[] t2 = new int[modLen];
|
||||
for (int i=0; i < table[0].length; i++)
|
||||
t2[i+offset] = table[0][i];
|
||||
System.arraycopy(table[0], 0, t2, offset, table[0].length);
|
||||
table[0] = t2;
|
||||
}
|
||||
|
||||
// Set b to the square of the base
|
||||
int[] b = squareToLen(table[0], modLen, null);
|
||||
b = montReduce(b, mod, modLen, inv);
|
||||
int[] b = montgomerySquare(table[0], mod, modLen, inv, null);
|
||||
|
||||
// Set t to high half of b
|
||||
int[] t = Arrays.copyOf(b, modLen);
|
||||
|
||||
// Fill in the table with odd powers of the base
|
||||
for (int i=1; i < tblmask; i++) {
|
||||
int[] prod = multiplyToLen(t, modLen, table[i-1], modLen, null);
|
||||
table[i] = montReduce(prod, mod, modLen, inv);
|
||||
table[i] = montgomeryMultiply(t, table[i-1], mod, modLen, inv, null);
|
||||
}
|
||||
|
||||
// Pre load the window that slides over the exponent
|
||||
@ -2800,8 +2915,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
isone = false;
|
||||
} else {
|
||||
t = b;
|
||||
a = multiplyToLen(t, modLen, mult, modLen, a);
|
||||
a = montReduce(a, mod, modLen, inv);
|
||||
a = montgomeryMultiply(t, mult, mod, modLen, inv, a);
|
||||
t = a; a = b; b = t;
|
||||
}
|
||||
}
|
||||
@ -2813,8 +2927,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
// Square the input
|
||||
if (!isone) {
|
||||
t = b;
|
||||
a = squareToLen(t, modLen, a);
|
||||
a = montReduce(a, mod, modLen, inv);
|
||||
a = montgomerySquare(t, mod, modLen, inv, a);
|
||||
t = a; a = b; b = t;
|
||||
}
|
||||
}
|
||||
@ -2823,7 +2936,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
int[] t2 = new int[2*modLen];
|
||||
System.arraycopy(b, 0, t2, modLen, modLen);
|
||||
|
||||
b = montReduce(t2, mod, modLen, inv);
|
||||
b = montReduce(t2, mod, modLen, (int)inv);
|
||||
|
||||
t2 = Arrays.copyOf(b, modLen);
|
||||
|
||||
@ -2916,6 +3029,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
/**
|
||||
* Java Runtime may use intrinsic for this method.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
private static int implMulAdd(int[] out, int[] in, int offset, int len, int k) {
|
||||
long kLong = k & LONG_MASK;
|
||||
long carry = 0;
|
||||
|
@ -2064,6 +2064,21 @@ class MutableBigInteger {
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the multiplicative inverse of val mod 2^64. Assumes val is odd.
|
||||
*/
|
||||
static long inverseMod64(long val) {
|
||||
// Newton's iteration!
|
||||
long t = val;
|
||||
t *= 2 - val*t;
|
||||
t *= 2 - val*t;
|
||||
t *= 2 - val*t;
|
||||
t *= 2 - val*t;
|
||||
t *= 2 - val*t;
|
||||
assert(t * val == 1);
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the multiplicative inverse of 2^k mod mod, where mod is odd.
|
||||
*/
|
||||
|
@ -26,6 +26,7 @@
|
||||
package java.nio;
|
||||
|
||||
import java.util.Spliterator;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* A container for data of a specific primitive type.
|
||||
@ -535,6 +536,7 @@ public abstract class Buffer {
|
||||
* IndexOutOfBoundsException} if it is not smaller than the limit
|
||||
* or is smaller than zero.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
final int checkIndex(int i) { // package-private
|
||||
if ((i < 0) || (i >= limit))
|
||||
throw new IndexOutOfBoundsException();
|
||||
|
@ -42,6 +42,7 @@ import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* This class contains various methods for manipulating arrays (such as
|
||||
@ -2654,6 +2655,7 @@ public class Arrays {
|
||||
* @param a2 the other array to be tested for equality
|
||||
* @return <tt>true</tt> if the two arrays are equal
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static boolean equals(char[] a, char[] a2) {
|
||||
if (a==a2)
|
||||
return true;
|
||||
@ -3205,6 +3207,7 @@ public class Arrays {
|
||||
* an array of class <tt>newType</tt>
|
||||
* @since 1.6
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] copy = ((Object)newType == (Object)Object[].class)
|
||||
@ -3474,6 +3477,7 @@ public class Arrays {
|
||||
* an array of class <tt>newType</tt>.
|
||||
* @since 1.6
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
|
||||
int newLength = to - from;
|
||||
if (newLength < 0)
|
||||
|
@ -31,6 +31,7 @@ import java.util.function.Consumer;
|
||||
import java.util.function.DoubleConsumer;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.LongConsumer;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* Utility methods for operating on and creating streams.
|
||||
@ -98,6 +99,7 @@ final class Streams {
|
||||
}
|
||||
|
||||
@Override
|
||||
@HotSpotIntrinsicCandidate
|
||||
public void forEachRemaining(IntConsumer consumer) {
|
||||
Objects.requireNonNull(consumer);
|
||||
|
||||
|
@ -26,7 +26,10 @@
|
||||
package java.util.zip;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
import sun.nio.ch.DirectBuffer;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* A class that can be used to compute the CRC-32 of a data stream.
|
||||
@ -123,9 +126,49 @@ class CRC32 implements Checksum {
|
||||
return (long)crc & 0xffffffffL;
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
private native static int update(int crc, int b);
|
||||
private native static int updateBytes(int crc, byte[] b, int off, int len);
|
||||
|
||||
private native static int updateByteBuffer(int adler, long addr,
|
||||
int off, int len);
|
||||
private static int updateBytes(int crc, byte[] b, int off, int len) {
|
||||
updateBytesCheck(b, off, len);
|
||||
return updateBytes0(crc, b, off, len);
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
private native static int updateBytes0(int crc, byte[] b, int off, int len);
|
||||
|
||||
private static void updateBytesCheck(byte[] b, int off, int len) {
|
||||
if (len <= 0) {
|
||||
return; // not an error because updateBytesImpl won't execute if len <= 0
|
||||
}
|
||||
|
||||
Objects.requireNonNull(b);
|
||||
|
||||
if (off < 0 || off >= b.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(off);
|
||||
}
|
||||
|
||||
int endIndex = off + len - 1;
|
||||
if (endIndex < 0 || endIndex >= b.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(endIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static int updateByteBuffer(int alder, long addr,
|
||||
int off, int len) {
|
||||
updateByteBufferCheck(addr);
|
||||
return updateByteBuffer0(alder, addr, off, len);
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
private native static int updateByteBuffer0(int alder, long addr,
|
||||
int off, int len);
|
||||
|
||||
private static void updateByteBufferCheck(long addr) {
|
||||
// Performs only a null check because bounds checks
|
||||
// are not easy to do on raw addresses.
|
||||
if (addr == 0L) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ package java.util.zip;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import sun.misc.Unsafe;
|
||||
import sun.nio.ch.DirectBuffer;
|
||||
|
||||
@ -204,6 +206,7 @@ public final class CRC32C implements Checksum {
|
||||
/**
|
||||
* Updates the CRC-32C checksum with the specified array of bytes.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
private static int updateBytes(int crc, byte[] b, int off, int end) {
|
||||
|
||||
// Do only byte reads for arrays so short they can't be aligned
|
||||
@ -278,6 +281,7 @@ public final class CRC32C implements Checksum {
|
||||
/**
|
||||
* Updates the CRC-32C checksum reading from the specified address.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
private static int updateDirectByteBuffer(int crc, long address,
|
||||
int off, int end) {
|
||||
|
||||
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* The {@code @HotSpotIntrinsicCandidate} annotation is specific to the Oracle Java
|
||||
* HotSpot Virtual Machine implementation and indicates that an annotated method
|
||||
* may be (but is not guaranteed to be) intrinsified by the HotSpot VM. A method
|
||||
* is intrinsified if the HotSpot VM replaces the annotated method with hand-written
|
||||
* assembly and/or hand-written compiler IR -- a compiler intrinsic -- to improve
|
||||
* performance. The {@code @HotSpotIntrinsicCandidate} annotation is internal to the
|
||||
* Java libraries and is therefore not supposed to have any relevance for application
|
||||
* code.
|
||||
*
|
||||
* Maintainers of the Java libraries must consider the following when
|
||||
* modifying methods annotated with {@code @HotSpotIntrinsicCandidate}.
|
||||
*
|
||||
* <ul>
|
||||
* <li>When modifying a method annotated with {@code @HotSpotIntrinsicCandidate},
|
||||
* the corresponding intrinsic code in the HotSpot VM implementation must be
|
||||
* updated to match the semantics of the annotated method.</li>
|
||||
* <li>For some annotated methods, the corresponding intrinsic may omit some low-level
|
||||
* checks that would be performed as a matter of course if the intrinsic is implemented
|
||||
* using Java bytecodes. This is because individual Java bytecodes implicitly check
|
||||
* for exceptions like {@code NullPointerException} and {@code ArrayStoreException}.
|
||||
* If such a method is replaced by an intrinsic coded in assembly language, any
|
||||
* checks performed as a matter of normal bytecode operation must be performed
|
||||
* before entry into the assembly code. These checks must be performed, as
|
||||
* appropriate, on all arguments to the intrinsic, and on other values (if any) obtained
|
||||
* by the intrinsic through those arguments. The checks may be deduced by inspecting
|
||||
* the non-intrinsic Java code for the method, and determining exactly which exceptions
|
||||
* may be thrown by the code, including undeclared implicit {@code RuntimeException}s.
|
||||
* Therefore, depending on the data accesses performed by the intrinsic,
|
||||
* the checks may include:
|
||||
*
|
||||
* <ul>
|
||||
* <li>null checks on references</li>
|
||||
* <li>range checks on primitive values used as array indexes</li>
|
||||
* <li>other validity checks on primitive values (e.g., for divide-by-zero conditions)</li>
|
||||
* <li>store checks on reference values stored into arrays</li>
|
||||
* <li>array length checks on arrays indexed from within the intrinsic</li>
|
||||
* <li>reference casts (when formal parameters are {@code Object} or some other weak type)</li>
|
||||
* </ul>
|
||||
*
|
||||
* </li>
|
||||
*
|
||||
* <li>Note that the receiver value ({@code this}) is passed as a extra argument
|
||||
* to all non-static methods. If a non-static method is an intrinsic, the receiver
|
||||
* value does not need a null check, but (as stated above) any values loaded by the
|
||||
* intrinsic from object fields must also be checked. As a matter of clarity, it is
|
||||
* better to make intrinisics be static methods, to make the dependency on {@code this}
|
||||
* clear. Also, it is better to explicitly load all required values from object
|
||||
* fields before entering the intrinsic code, and pass those values as explicit arguments.
|
||||
* First, this may be necessary for null checks (or other checks). Second, if the
|
||||
* intrinsic reloads the values from fields and operates on those without checks,
|
||||
* race conditions may be able to introduce unchecked invalid values into the intrinsic.
|
||||
* If the intrinsic needs to store a value back to an object field, that value should be
|
||||
* returned explicitly from the intrinsic; if there are multiple return values, coders
|
||||
* should consider buffering them in an array. Removing field access from intrinsics
|
||||
* not only clarifies the interface with between the JVM and JDK; it also helps decouple
|
||||
* the HotSpot and JDK implementations, since if JDK code before and after the intrinsic
|
||||
* manages all field accesses, then intrinsics can be coded to be agnostic of object
|
||||
* layouts.</li>
|
||||
*
|
||||
* Maintainers of the HotSpot VM must consider the following when modifying
|
||||
* intrinsics.
|
||||
*
|
||||
* <ul>
|
||||
* <li>When adding a new intrinsic, make sure that the corresponding method
|
||||
* in the Java libraries is annotated with {@code @HotSpotIntrinsicCandidate}
|
||||
* and that all possible call sequences that result in calling the intrinsic contain
|
||||
* the checks omitted by the intrinsic (if any).</li>
|
||||
* <li>When modifying an existing intrinsic, the Java libraries must be updated
|
||||
* to match the semantics of the intrinsic and to execute all checks omitted
|
||||
* by the intrinsic (if any).</li>
|
||||
* </ul>
|
||||
*
|
||||
* Persons not directly involved with maintaining the Java libraries or the
|
||||
* HotSpot VM can safely ignore the fact that a method is annotated with
|
||||
* {@code @HotSpotIntrinsicCandidate}.
|
||||
*
|
||||
* The HotSpot VM defines (internally) a list of intrinsics. Not all intrinsic
|
||||
* are available on all platforms supported by the HotSpot VM. Furthermore,
|
||||
* the availability of an intrinsic on a given platform depends on the
|
||||
* configuration of the HotSpot VM (e.g., the set of VM flags enabled).
|
||||
* Therefore, annotating a method with {@code @HotSpotIntrinsicCandidate} does
|
||||
* not guarantee that the marked method is intrinsified by the HotSpot VM.
|
||||
*
|
||||
* If the {@code CheckIntrinsics} VM flag is enabled, the HotSpot VM checks
|
||||
* (when loading a class) that (1) all methods of that class that are also on
|
||||
* the VM's list of intrinsics are annotated with {@code @HotSpotIntrinsicCandidate}
|
||||
* and that (2) for all methods of that class annotated with
|
||||
* {@code @HotSpotIntrinsicCandidate} there is an intrinsic in the list.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface HotSpotIntrinsicCandidate {
|
||||
}
|
@ -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);
|
||||
Stream<Entry> entries();
|
||||
|
||||
/**
|
||||
* Visits all entries in the Archive.
|
||||
* Open the archive
|
||||
*/
|
||||
void visitEntries(Consumer<Entry> consumer) ;
|
||||
void open() throws IOException;
|
||||
|
||||
/**
|
||||
* An entries in the Archive.
|
||||
* Close the archive
|
||||
*/
|
||||
interface Entry {
|
||||
String getName();
|
||||
InputStream getInputStream();
|
||||
boolean isDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* A Consumer suitable for writing Entries from this Archive.
|
||||
*/
|
||||
Consumer<Entry> defaultImageWriter(Path path, OutputStream out);
|
||||
void close() throws IOException;
|
||||
}
|
||||
|
@ -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 class BasicImageReader {
|
||||
}
|
||||
|
||||
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 byte[] getResource(ImageLocation loc) throws IOException {
|
||||
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) {
|
||||
long offset = loc.getContentOffset();
|
||||
long compressedSize = loc.getCompressedSize();
|
||||
long uncompressedSize = loc.getUncompressedSize();
|
||||
assert compressedSize < Integer.MAX_VALUE;
|
||||
assert uncompressedSize < Integer.MAX_VALUE;
|
||||
|
||||
if (compressedSize == 0) {
|
||||
return preader.read((int)loc.getUncompressedSize(),
|
||||
indexSize + loc.getContentOffset());
|
||||
} else {
|
||||
byte[] buf = preader.read((int)compressedSize,
|
||||
indexSize + loc.getContentOffset());
|
||||
return ImageFile.Compressor.decompress(buf);
|
||||
if (substrate.supportsDataBuffer() && compressedSize == 0) {
|
||||
return getBufferBytes(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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
public List<String> getNames(byte[] bytes) {
|
||||
IntBuffer buffer = ByteBuffer.wrap(bytes).asIntBuffer();
|
||||
List<String> names = new ArrayList<>();
|
||||
|
||||
while (buffer.hasRemaining()) {
|
||||
int offset = buffer.get();
|
||||
names.add(getString(offset));
|
||||
if (substrate.supportsDataBuffer() && compressedSize == 0) {
|
||||
return substrate.getDataBuffer(offset, uncompressedSize);
|
||||
}
|
||||
|
||||
return names;
|
||||
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 ByteBuffer getResourceBuffer(String name) {
|
||||
ImageLocation location = findLocation(name);
|
||||
|
||||
return location != null ? getResourceBuffer(location) : null;
|
||||
}
|
||||
|
||||
public InputStream getResourceStream(ImageLocation loc) {
|
||||
byte[] bytes = getResource(loc);
|
||||
|
||||
return new ByteArrayInputStream(bytes);
|
||||
}
|
||||
|
||||
public InputStream getResourceStream(String name) {
|
||||
ImageLocation location = findLocation(name);
|
||||
|
||||
return location != null ? getResourceStream(location) : null;
|
||||
}
|
||||
}
|
||||
|
@ -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 final class BasicImageWriter {
|
||||
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 @@ public final class BasicImageWriter {
|
||||
this.allIndexStream = new ImageStream(byteOrder);
|
||||
}
|
||||
|
||||
public ByteOrder getByteOrder() {
|
||||
return byteOrder;
|
||||
}
|
||||
|
||||
public int addString(String string) {
|
||||
return addString(new UTF8String(string));
|
||||
}
|
||||
@ -109,104 +76,48 @@ public final class BasicImageWriter {
|
||||
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];
|
||||
PerfectHashBuilder<ImageLocationWriter> builder =
|
||||
new PerfectHashBuilder<>(
|
||||
new PerfectHashBuilder.Entry<ImageLocationWriter>().getClass(),
|
||||
new PerfectHashBuilder.Bucket<ImageLocationWriter>().getClass());
|
||||
|
||||
ImageBucket[] sorted = createBuckets();
|
||||
|
||||
int free = 0;
|
||||
|
||||
for (ImageBucket bucket : sorted) {
|
||||
if (bucket.getSize() != 1) {
|
||||
if (!packCollidedEntries(bucket, count)) {
|
||||
count = (count + 1) | 1;
|
||||
|
||||
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);
|
||||
builder.generate();
|
||||
|
||||
return sorted;
|
||||
}
|
||||
length = builder.getCount();
|
||||
redirect = builder.getRedirect();
|
||||
PerfectHashBuilder.Entry<ImageLocationWriter>[] order = builder.getOrder();
|
||||
locations = new ImageLocationWriter[length];
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
undo.clear();
|
||||
base++;
|
||||
|
||||
if (base == 0) {
|
||||
base = 1;
|
||||
}
|
||||
|
||||
if (++retry > RETRY_LIMIT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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 @@ public final class BasicImageWriter {
|
||||
}
|
||||
|
||||
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 @@ public final class BasicImageWriter {
|
||||
}
|
||||
|
||||
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 @@ public final class BasicImageWriter {
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 "";
|
||||
}
|
||||
}
|
||||
}
|
@ -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 @@ public final class ImageHeader {
|
||||
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() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
public final class ImageLocation extends ImageLocationBase {
|
||||
ImageLocation(long[] attributes, ImageStringsReader strings) {
|
||||
super(attributes, strings);
|
||||
}
|
||||
|
||||
void writeTo(ImageStream stream) {
|
||||
compress();
|
||||
locationOffset = stream.getPosition();
|
||||
stream.put(bytes, 0, bytes.length);
|
||||
}
|
||||
static ImageLocation readFrom(BasicImageReader reader, int offset) {
|
||||
long[] attributes = reader.getAttributes(offset);
|
||||
ImageStringsReader strings = reader.getStrings();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -26,12 +26,10 @@ package jdk.internal.jimage;
|
||||
|
||||
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.HashMap;
|
||||
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 @@ public class ImageReader extends BasicImageReader {
|
||||
// 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 @@ public class ImageReader extends BasicImageReader {
|
||||
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 @@ public class ImageReader extends BasicImageReader {
|
||||
// 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 @@ public class ImageReader extends BasicImageReader {
|
||||
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 @@ public class ImageReader extends BasicImageReader {
|
||||
return (flags & ROOT_DIR) != 0;
|
||||
}
|
||||
|
||||
public final void setIsModuleDir() {
|
||||
flags |= MODULE_DIR;
|
||||
public final void setIsPackagesDir() {
|
||||
flags |= PACKAGES_DIR;
|
||||
}
|
||||
|
||||
public final boolean isModuleDir() {
|
||||
return (flags & MODULE_DIR) != 0;
|
||||
public final boolean isPackagesDir() {
|
||||
return (flags & PACKAGES_DIR) != 0;
|
||||
}
|
||||
|
||||
public final void setIsMetaInfDir() {
|
||||
flags |= METAINF_DIR;
|
||||
public final void setIsModulesDir() {
|
||||
flags |= MODULES_DIR;
|
||||
}
|
||||
|
||||
public final boolean isMetaInfDir() {
|
||||
return (flags & METAINF_DIR) != 0;
|
||||
}
|
||||
|
||||
public final void setIsTopLevelPackageDir() {
|
||||
flags |= TOPLEVEL_PKG_DIR;
|
||||
}
|
||||
|
||||
public final boolean isTopLevelPackageDir() {
|
||||
return (flags & TOPLEVEL_PKG_DIR) != 0;
|
||||
}
|
||||
|
||||
public final void setIsHidden() {
|
||||
flags |= HIDDEN;
|
||||
}
|
||||
|
||||
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 @@ public class ImageReader extends BasicImageReader {
|
||||
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 @@ public class ImageReader extends BasicImageReader {
|
||||
}
|
||||
|
||||
// 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 @@ public class ImageReader extends BasicImageReader {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getChildren() {
|
||||
return Collections.unmodifiableList(children);
|
||||
}
|
||||
@ -281,19 +322,33 @@ public class ImageReader extends BasicImageReader {
|
||||
|
||||
// "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 @@ public class ImageReader extends BasicImageReader {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 @@ public class ImageReader extends BasicImageReader {
|
||||
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 @@ public class ImageReader extends BasicImageReader {
|
||||
// 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);
|
||||
}
|
||||
|
||||
fillPackageModuleInfo();
|
||||
// /modules dir
|
||||
modulesDir = newDirectory(rootDir, MODULES_STRING);
|
||||
modulesDir.setIsModulesDir();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
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<>();
|
||||
|
||||
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 @@ public class ImageReader extends BasicImageReader {
|
||||
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());
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -72,7 +72,7 @@ class ImageStream {
|
||||
return this;
|
||||
}
|
||||
|
||||
private void ensure(int needs) {
|
||||
void ensure(int needs) {
|
||||
assert 0 <= needs : "Negative needs";
|
||||
|
||||
if (needs > buffer.remaining()) {
|
||||
|
@ -25,83 +25,8 @@
|
||||
|
||||
package jdk.internal.jimage;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
interface ImageStrings {
|
||||
public UTF8String get(int offset);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -22,46 +22,23 @@
|
||||
* 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;
|
||||
class ImageStringsReader implements ImageStrings {
|
||||
private final BasicImageReader reader;
|
||||
|
||||
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;
|
||||
ImageStringsReader(BasicImageReader reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s uncompressed size %d compressed size %d", name, size, csize);
|
||||
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");
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -22,41 +22,24 @@
|
||||
* 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.Closeable;
|
||||
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;
|
||||
}
|
||||
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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -29,14 +29,18 @@ import java.nio.charset.Charset;
|
||||
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 @@ public final class UTF8String implements CharSequence {
|
||||
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 @@ public final class UTF8String implements CharSequence {
|
||||
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 @@ public final class UTF8String implements CharSequence {
|
||||
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 final class UTF8String implements CharSequence {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ final class JrtDirectoryStream implements DirectoryStream<Path> {
|
||||
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
|
||||
|
@ -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 @@ final class JrtFileAttributeView implements BasicFileAttributeView
|
||||
|
||||
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 @@ final class JrtFileAttributeView implements BasicFileAttributeView
|
||||
@Override
|
||||
public JrtFileAttributes readAttributes() throws IOException
|
||||
{
|
||||
return path.getAttributes();
|
||||
return path.getAttributes(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -76,12 +76,12 @@ final class JrtFileAttributes implements BasicFileAttributes
|
||||
|
||||
@Override
|
||||
public boolean isSymbolicLink() {
|
||||
return false;
|
||||
return node.isLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fileKey() {
|
||||
return null;
|
||||
return node.resolveLink(true);
|
||||
}
|
||||
|
||||
///////// jrt entry attributes ///////////
|
||||
|
@ -31,9 +31,9 @@ import java.io.OutputStream;
|
||||
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.OpenOption;
|
||||
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.Iterator;
|
||||
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 @@ import jdk.internal.jimage.UTF8String;
|
||||
*/
|
||||
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 @@ class JrtFileSystem extends FileSystem {
|
||||
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 @@ class JrtFileSystem extends FileSystem {
|
||||
synchronized(this) {
|
||||
isOpen = false;
|
||||
|
||||
// close all image readers and null out
|
||||
// close all image reader and null out
|
||||
bootImage.close();
|
||||
extImage.close();
|
||||
appImage.close();
|
||||
bootImage = null;
|
||||
extImage.close();
|
||||
extImage = null;
|
||||
appImage.close();
|
||||
appImage = null;
|
||||
}
|
||||
}
|
||||
@ -289,21 +289,52 @@ class JrtFileSystem extends FileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
image = extImage;
|
||||
node = extImage.findNode(path);
|
||||
image = extImage;
|
||||
}
|
||||
if (node == null) {
|
||||
image = appImage;
|
||||
node = appImage.findNode(path);
|
||||
image = appImage;
|
||||
}
|
||||
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 @@ class JrtFileSystem extends FileSystem {
|
||||
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 @@ class JrtFileSystem extends FileSystem {
|
||||
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 @@ class JrtFileSystem extends FileSystem {
|
||||
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 @@ class JrtFileSystem extends FileSystem {
|
||||
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 @@ class JrtFileSystem extends FileSystem {
|
||||
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();
|
||||
|
@ -145,6 +145,11 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
toJrtPath(path).checkAccess(modes);
|
||||
}
|
||||
|
||||
@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 final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
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 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
|
||||
throws IOException
|
||||
{
|
||||
if (type == BasicFileAttributes.class || type == JrtFileAttributes.class)
|
||||
return (A)toJrtPath(path).getAttributes();
|
||||
return (A)toJrtPath(path).getAttributes(options);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,10 @@ final class JrtPath implements Path {
|
||||
this.path = normalize(path);
|
||||
}
|
||||
|
||||
byte[] getName() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JrtPath getRoot() {
|
||||
if (this.isAbsolute())
|
||||
@ -140,10 +144,19 @@ final class JrtPath implements Path {
|
||||
@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 @@ final class JrtPath implements Path {
|
||||
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 @@ final class JrtPath implements Path {
|
||||
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 @@ final class JrtPath implements Path {
|
||||
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 @@ final class JrtPath implements Path {
|
||||
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,
|
||||
|
@ -42,6 +42,7 @@ final class SystemImages {
|
||||
static final Path bootImagePath;
|
||||
static final Path extImagePath;
|
||||
static final Path appImagePath;
|
||||
|
||||
static {
|
||||
PrivilegedAction<String> pa = SystemImages::findHome;
|
||||
RUNTIME_HOME = AccessController.doPrivileged(pa);
|
||||
|
@ -31,6 +31,8 @@ import java.security.ProtectionDomain;
|
||||
import sun.reflect.CallerSensitive;
|
||||
import sun.reflect.Reflection;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
|
||||
/**
|
||||
* A collection of methods for performing low-level, unsafe operations.
|
||||
@ -148,6 +150,7 @@ public final class Unsafe {
|
||||
* @throws RuntimeException No defined exceptions are thrown, not even
|
||||
* {@link NullPointerException}
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native int getInt(Object o, long offset);
|
||||
|
||||
/**
|
||||
@ -170,12 +173,14 @@ public final class Unsafe {
|
||||
* @throws RuntimeException No defined exceptions are thrown, not even
|
||||
* {@link NullPointerException}
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putInt(Object o, long offset, int x);
|
||||
|
||||
/**
|
||||
* Fetches a reference value from a given Java variable.
|
||||
* @see #getInt(Object, long)
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native Object getObject(Object o, long offset);
|
||||
|
||||
/**
|
||||
@ -188,35 +193,50 @@ public final class Unsafe {
|
||||
* are updated.
|
||||
* @see #putInt(Object, long, int)
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putObject(Object o, long offset, Object x);
|
||||
|
||||
/** @see #getInt(Object, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native boolean getBoolean(Object o, long offset);
|
||||
/** @see #putInt(Object, long, int) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putBoolean(Object o, long offset, boolean x);
|
||||
/** @see #getInt(Object, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native byte getByte(Object o, long offset);
|
||||
/** @see #putInt(Object, long, int) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putByte(Object o, long offset, byte x);
|
||||
/** @see #getInt(Object, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native short getShort(Object o, long offset);
|
||||
/** @see #putInt(Object, long, int) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putShort(Object o, long offset, short x);
|
||||
/** @see #getInt(Object, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native char getChar(Object o, long offset);
|
||||
/** @see #putInt(Object, long, int) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putChar(Object o, long offset, char x);
|
||||
/** @see #getInt(Object, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native long getLong(Object o, long offset);
|
||||
/** @see #putInt(Object, long, int) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putLong(Object o, long offset, long x);
|
||||
/** @see #getInt(Object, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native float getFloat(Object o, long offset);
|
||||
/** @see #putInt(Object, long, int) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putFloat(Object o, long offset, float x);
|
||||
/** @see #getInt(Object, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native double getDouble(Object o, long offset);
|
||||
/** @see #putInt(Object, long, int) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putDouble(Object o, long offset, double x);
|
||||
|
||||
// These read VM internal data.
|
||||
@ -257,6 +277,7 @@ public final class Unsafe {
|
||||
*
|
||||
* @see #allocateMemory
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native byte getByte(long address);
|
||||
|
||||
/**
|
||||
@ -266,31 +287,44 @@ public final class Unsafe {
|
||||
*
|
||||
* @see #getByte(long)
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putByte(long address, byte x);
|
||||
|
||||
/** @see #getByte(long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native short getShort(long address);
|
||||
/** @see #putByte(long, byte) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putShort(long address, short x);
|
||||
/** @see #getByte(long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native char getChar(long address);
|
||||
/** @see #putByte(long, byte) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putChar(long address, char x);
|
||||
/** @see #getByte(long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native int getInt(long address);
|
||||
/** @see #putByte(long, byte) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putInt(long address, int x);
|
||||
/** @see #getByte(long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native long getLong(long address);
|
||||
/** @see #putByte(long, byte) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putLong(long address, long x);
|
||||
/** @see #getByte(long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native float getFloat(long address);
|
||||
/** @see #putByte(long, byte) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putFloat(long address, float x);
|
||||
/** @see #getByte(long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native double getDouble(long address);
|
||||
/** @see #putByte(long, byte) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putDouble(long address, double x);
|
||||
|
||||
/**
|
||||
@ -307,6 +341,7 @@ public final class Unsafe {
|
||||
*
|
||||
* @see #allocateMemory
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native long getAddress(long address);
|
||||
|
||||
/**
|
||||
@ -319,6 +354,7 @@ public final class Unsafe {
|
||||
*
|
||||
* @see #getAddress(long)
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putAddress(long address, long x);
|
||||
|
||||
/// wrappers for malloc, realloc, free:
|
||||
@ -406,6 +442,7 @@ public final class Unsafe {
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void copyMemory(Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes);
|
||||
@ -651,6 +688,7 @@ public final class Unsafe {
|
||||
* Allocates an instance but does not run any constructor.
|
||||
* Initializes the class if it has not yet been.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native Object allocateInstance(Class<?> cls)
|
||||
throws InstantiationException;
|
||||
|
||||
@ -666,6 +704,7 @@ public final class Unsafe {
|
||||
*
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final native boolean compareAndSwapObject(Object o, long offset,
|
||||
Object expected,
|
||||
Object x);
|
||||
@ -679,6 +718,7 @@ public final class Unsafe {
|
||||
*
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final native boolean compareAndSwapInt(Object o, long offset,
|
||||
int expected,
|
||||
int x);
|
||||
@ -692,6 +732,7 @@ public final class Unsafe {
|
||||
*
|
||||
* @return {@code true} if successful
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final native boolean compareAndSwapLong(Object o, long offset,
|
||||
long expected,
|
||||
long x);
|
||||
@ -700,60 +741,78 @@ public final class Unsafe {
|
||||
* Fetches a reference value from a given Java variable, with volatile
|
||||
* load semantics. Otherwise identical to {@link #getObject(Object, long)}
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native Object getObjectVolatile(Object o, long offset);
|
||||
|
||||
/**
|
||||
* Stores a reference value into a given Java variable, with
|
||||
* volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)}
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putObjectVolatile(Object o, long offset, Object x);
|
||||
|
||||
/** Volatile version of {@link #getInt(Object, long)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native int getIntVolatile(Object o, long offset);
|
||||
|
||||
/** Volatile version of {@link #putInt(Object, long, int)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putIntVolatile(Object o, long offset, int x);
|
||||
|
||||
/** Volatile version of {@link #getBoolean(Object, long)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native boolean getBooleanVolatile(Object o, long offset);
|
||||
|
||||
/** Volatile version of {@link #putBoolean(Object, long, boolean)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putBooleanVolatile(Object o, long offset, boolean x);
|
||||
|
||||
/** Volatile version of {@link #getByte(Object, long)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native byte getByteVolatile(Object o, long offset);
|
||||
|
||||
/** Volatile version of {@link #putByte(Object, long, byte)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putByteVolatile(Object o, long offset, byte x);
|
||||
|
||||
/** Volatile version of {@link #getShort(Object, long)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native short getShortVolatile(Object o, long offset);
|
||||
|
||||
/** Volatile version of {@link #putShort(Object, long, short)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putShortVolatile(Object o, long offset, short x);
|
||||
|
||||
/** Volatile version of {@link #getChar(Object, long)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native char getCharVolatile(Object o, long offset);
|
||||
|
||||
/** Volatile version of {@link #putChar(Object, long, char)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putCharVolatile(Object o, long offset, char x);
|
||||
|
||||
/** Volatile version of {@link #getLong(Object, long)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native long getLongVolatile(Object o, long offset);
|
||||
|
||||
/** Volatile version of {@link #putLong(Object, long, long)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putLongVolatile(Object o, long offset, long x);
|
||||
|
||||
/** Volatile version of {@link #getFloat(Object, long)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native float getFloatVolatile(Object o, long offset);
|
||||
|
||||
/** Volatile version of {@link #putFloat(Object, long, float)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putFloatVolatile(Object o, long offset, float x);
|
||||
|
||||
/** Volatile version of {@link #getDouble(Object, long)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native double getDoubleVolatile(Object o, long offset);
|
||||
|
||||
/** Volatile version of {@link #putDouble(Object, long, double)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putDoubleVolatile(Object o, long offset, double x);
|
||||
|
||||
/**
|
||||
@ -765,12 +824,15 @@ public final class Unsafe {
|
||||
*
|
||||
* Corresponds to C11 atomic_store_explicit(..., memory_order_release).
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putOrderedObject(Object o, long offset, Object x);
|
||||
|
||||
/** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putOrderedInt(Object o, long offset, int x);
|
||||
|
||||
/** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putOrderedLong(Object o, long offset, long x);
|
||||
|
||||
/**
|
||||
@ -785,6 +847,7 @@ public final class Unsafe {
|
||||
*
|
||||
* @param thread the thread to unpark.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void unpark(Object thread);
|
||||
|
||||
/**
|
||||
@ -798,6 +861,7 @@ public final class Unsafe {
|
||||
* because {@code unpark} is, so it would be strange to place it
|
||||
* elsewhere.
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void park(boolean isAbsolute, long time);
|
||||
|
||||
/**
|
||||
@ -831,6 +895,7 @@ public final class Unsafe {
|
||||
* @return the previous value
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final int getAndAddInt(Object o, long offset, int delta) {
|
||||
int v;
|
||||
do {
|
||||
@ -850,6 +915,7 @@ public final class Unsafe {
|
||||
* @return the previous value
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final long getAndAddLong(Object o, long offset, long delta) {
|
||||
long v;
|
||||
do {
|
||||
@ -869,6 +935,7 @@ public final class Unsafe {
|
||||
* @return the previous value
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final int getAndSetInt(Object o, long offset, int newValue) {
|
||||
int v;
|
||||
do {
|
||||
@ -888,6 +955,7 @@ public final class Unsafe {
|
||||
* @return the previous value
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final long getAndSetLong(Object o, long offset, long newValue) {
|
||||
long v;
|
||||
do {
|
||||
@ -907,6 +975,7 @@ public final class Unsafe {
|
||||
* @return the previous value
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final Object getAndSetObject(Object o, long offset, Object newValue) {
|
||||
Object v;
|
||||
do {
|
||||
@ -928,6 +997,7 @@ public final class Unsafe {
|
||||
* provide a LoadLoad barrier also provide a LoadStore barrier for free.
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void loadFence();
|
||||
|
||||
/**
|
||||
@ -942,6 +1012,7 @@ public final class Unsafe {
|
||||
* provide a StoreStore barrier also provide a LoadStore barrier for free.
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void storeFence();
|
||||
|
||||
/**
|
||||
@ -953,6 +1024,7 @@ public final class Unsafe {
|
||||
* Corresponds to C11 atomic_thread_fence(memory_order_seq_cst).
|
||||
* @since 1.8
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void fullFence();
|
||||
|
||||
/**
|
||||
@ -1010,6 +1082,7 @@ public final class Unsafe {
|
||||
* {@link NullPointerException}
|
||||
* @since 1.9
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final long getLongUnaligned(Object o, long offset) {
|
||||
if ((offset & 7) == 0) {
|
||||
return getLong(o, offset);
|
||||
@ -1048,6 +1121,7 @@ public final class Unsafe {
|
||||
}
|
||||
|
||||
/** @see #getLongUnaligned(Object, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final int getIntUnaligned(Object o, long offset) {
|
||||
if ((offset & 3) == 0) {
|
||||
return getInt(o, offset);
|
||||
@ -1067,6 +1141,7 @@ public final class Unsafe {
|
||||
}
|
||||
|
||||
/** @see #getLongUnaligned(Object, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final short getShortUnaligned(Object o, long offset) {
|
||||
if ((offset & 1) == 0) {
|
||||
return getShort(o, offset);
|
||||
@ -1081,9 +1156,11 @@ public final class Unsafe {
|
||||
}
|
||||
|
||||
/** @see #getLongUnaligned(Object, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final char getCharUnaligned(Object o, long offset) {
|
||||
return (char)getShortUnaligned(o, offset);
|
||||
}
|
||||
|
||||
/** @see #getLongUnaligned(Object, long, boolean) */
|
||||
public final char getCharUnaligned(Object o, long offset, boolean bigEndian) {
|
||||
return convEndian(bigEndian, getCharUnaligned(o, offset));
|
||||
@ -1117,6 +1194,7 @@ public final class Unsafe {
|
||||
* {@link NullPointerException}
|
||||
* @since 1.9
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final void putLongUnaligned(Object o, long offset, long x) {
|
||||
if ((offset & 7) == 0) {
|
||||
putLong(o, offset, x);
|
||||
@ -1142,6 +1220,7 @@ public final class Unsafe {
|
||||
(byte)(x >>> 56));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* As {@link #putLongUnaligned(Object, long, long)} but with an additional
|
||||
* argument which specifies the endianness of the value as stored in memory.
|
||||
@ -1158,6 +1237,7 @@ public final class Unsafe {
|
||||
}
|
||||
|
||||
/** @see #putLongUnaligned(Object, long, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final void putIntUnaligned(Object o, long offset, int x) {
|
||||
if ((offset & 3) == 0) {
|
||||
putInt(o, offset, x);
|
||||
@ -1179,6 +1259,7 @@ public final class Unsafe {
|
||||
}
|
||||
|
||||
/** @see #putLongUnaligned(Object, long, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final void putShortUnaligned(Object o, long offset, short x) {
|
||||
if ((offset & 1) == 0) {
|
||||
putShort(o, offset, x);
|
||||
@ -1194,6 +1275,7 @@ public final class Unsafe {
|
||||
}
|
||||
|
||||
/** @see #putLongUnaligned(Object, long, long) */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public final void putCharUnaligned(Object o, long offset, char x) {
|
||||
putShortUnaligned(o, offset, (short)x);
|
||||
}
|
||||
|
@ -32,6 +32,9 @@ import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
class ISO_8859_1
|
||||
extends Charset
|
||||
@ -147,9 +150,16 @@ class ISO_8859_1
|
||||
|
||||
private final Surrogate.Parser sgp = new Surrogate.Parser();
|
||||
|
||||
// JVM may replace this method with intrinsic code.
|
||||
// Method possible replaced with a compiler intrinsic.
|
||||
private static int encodeISOArray(char[] sa, int sp,
|
||||
byte[] da, int dp, int len)
|
||||
byte[] da, int dp, int len) {
|
||||
encodeISOArrayCheck(sa, sp, da, dp, len);
|
||||
return implEncodeISOArray(sa, sp, da, dp, len);
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
private static int implEncodeISOArray(char[] sa, int sp,
|
||||
byte[] da, int dp, int len)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < len; i++) {
|
||||
@ -161,6 +171,34 @@ class ISO_8859_1
|
||||
return i;
|
||||
}
|
||||
|
||||
private static void encodeISOArrayCheck(char[] sa, int sp,
|
||||
byte[] da, int dp, int len) {
|
||||
if (len <= 0) {
|
||||
return; // not an error because encodeISOArrayImpl won't execute if len <= 0
|
||||
}
|
||||
|
||||
Objects.requireNonNull(sa);
|
||||
Objects.requireNonNull(da);
|
||||
|
||||
if (sp < 0 || sp >= sa.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(sp);
|
||||
}
|
||||
|
||||
if (dp < 0 || dp >= da.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(dp);
|
||||
}
|
||||
|
||||
int endIndexSP = sp + len - 1;
|
||||
if (endIndexSP < 0 || endIndexSP >= sa.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(endIndexSP);
|
||||
}
|
||||
|
||||
int endIndexDP = dp + len - 1;
|
||||
if (endIndexDP < 0 || endIndexDP >= da.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(endIndexDP);
|
||||
}
|
||||
}
|
||||
|
||||
private CoderResult encodeArrayLoop(CharBuffer src,
|
||||
ByteBuffer dst)
|
||||
{
|
||||
|
@ -28,6 +28,7 @@ package sun.reflect;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/** Common utility routines used by both java.lang and
|
||||
java.lang.reflect */
|
||||
@ -56,6 +57,7 @@ public class Reflection {
|
||||
ignoring frames associated with java.lang.reflect.Method.invoke()
|
||||
and its implementation. */
|
||||
@CallerSensitive
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native Class<?> getCallerClass();
|
||||
|
||||
/**
|
||||
@ -74,6 +76,7 @@ public class Reflection {
|
||||
to compatibility reasons; see 4471811. Only the values of the
|
||||
low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
|
||||
valid. */
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static native int getClassAccessFlags(Class<?> c);
|
||||
|
||||
/** A quick "fast-path" check to try to avoid getCallerClass()
|
||||
|
@ -28,6 +28,9 @@ package sun.security.provider;
|
||||
import java.security.MessageDigestSpi;
|
||||
import java.security.DigestException;
|
||||
import java.security.ProviderException;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* Common base message digest implementation for the Sun provider.
|
||||
@ -136,12 +139,36 @@ abstract class DigestBase extends MessageDigestSpi implements Cloneable {
|
||||
|
||||
// compress complete blocks
|
||||
private int implCompressMultiBlock(byte[] b, int ofs, int limit) {
|
||||
implCompressMultiBlockCheck(b, ofs, limit);
|
||||
return implCompressMultiBlock0(b, ofs, limit);
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
private int implCompressMultiBlock0(byte[] b, int ofs, int limit) {
|
||||
for (; ofs <= limit; ofs += blockSize) {
|
||||
implCompress(b, ofs);
|
||||
}
|
||||
return ofs;
|
||||
}
|
||||
|
||||
private void implCompressMultiBlockCheck(byte[] b, int ofs, int limit) {
|
||||
if (limit < 0) {
|
||||
return; // not an error because implCompressMultiBlockImpl won't execute if limit < 0
|
||||
// and an exception is thrown if ofs < 0.
|
||||
}
|
||||
|
||||
Objects.requireNonNull(b);
|
||||
|
||||
if (ofs < 0 || ofs >= b.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(ofs);
|
||||
}
|
||||
|
||||
int endIndex = (limit / blockSize) * blockSize + blockSize - 1;
|
||||
if (endIndex >= b.length) {
|
||||
throw new ArrayIndexOutOfBoundsException(endIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// reset this object. See JCA doc.
|
||||
protected final void engineReset() {
|
||||
if (bytesProcessed == 0) {
|
||||
|
@ -25,7 +25,10 @@
|
||||
|
||||
package sun.security.provider;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static sun.security.provider.ByteArrayAccess.*;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/**
|
||||
* This class implements the Secure Hash Algorithm (SHA) developed by
|
||||
@ -114,8 +117,27 @@ public final class SHA extends DigestBase {
|
||||
* "old" NIST Secure Hash Algorithm.
|
||||
*/
|
||||
void implCompress(byte[] buf, int ofs) {
|
||||
b2iBig64(buf, ofs, W);
|
||||
implCompressCheck(buf, ofs);
|
||||
implCompress0(buf, ofs);
|
||||
}
|
||||
|
||||
private void implCompressCheck(byte[] buf, int ofs) {
|
||||
Objects.requireNonNull(buf);
|
||||
|
||||
// The checks performed by the method 'b2iBig64'
|
||||
// are sufficient for the case when the method
|
||||
// 'implCompressImpl' is replaced with a compiler
|
||||
// intrinsic.
|
||||
b2iBig64(buf, ofs, W);
|
||||
}
|
||||
|
||||
// The method 'implCompressImpl seems not to use its parameters.
|
||||
// The method can, however, be replaced with a compiler intrinsic
|
||||
// that operates directly on the array 'buf' (starting from
|
||||
// offset 'ofs') and not on array 'W', therefore 'buf' and 'ofs'
|
||||
// must be passed as parameter to the method.
|
||||
@HotSpotIntrinsicCandidate
|
||||
private void implCompress0(byte[] buf, int ofs) {
|
||||
// The first 16 ints have the byte stream, compute the rest of
|
||||
// the buffer
|
||||
for (int t = 16; t <= 79; t++) {
|
||||
|
@ -25,6 +25,9 @@
|
||||
|
||||
package sun.security.provider;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import static sun.security.provider.ByteArrayAccess.*;
|
||||
|
||||
/**
|
||||
@ -186,8 +189,27 @@ abstract class SHA2 extends DigestBase {
|
||||
* Process the current block to update the state variable state.
|
||||
*/
|
||||
void implCompress(byte[] buf, int ofs) {
|
||||
b2iBig64(buf, ofs, W);
|
||||
implCompressCheck(buf, ofs);
|
||||
implCompress0(buf, ofs);
|
||||
}
|
||||
|
||||
private void implCompressCheck(byte[] buf, int ofs) {
|
||||
Objects.requireNonNull(buf);
|
||||
|
||||
// The checks performed by the method 'b2iBig64'
|
||||
// are sufficient for the case when the method
|
||||
// 'implCompressImpl' is replaced with a compiler
|
||||
// intrinsic.
|
||||
b2iBig64(buf, ofs, W);
|
||||
}
|
||||
|
||||
// The method 'implCompressImpl' seems not to use its parameters.
|
||||
// The method can, however, be replaced with a compiler intrinsic
|
||||
// that operates directly on the array 'buf' (starting from
|
||||
// offset 'ofs') and not on array 'W', therefore 'buf' and 'ofs'
|
||||
// must be passed as parameter to the method.
|
||||
@HotSpotIntrinsicCandidate
|
||||
private void implCompress0(byte[] buf, int ofs) {
|
||||
// The first 16 ints are from the byte stream, compute the rest of
|
||||
// the W[]'s
|
||||
for (int t = 16; t < ITERATION; t++) {
|
||||
|
@ -26,8 +26,10 @@
|
||||
package sun.security.provider;
|
||||
|
||||
import java.security.*;
|
||||
import java.util.Objects;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import static sun.security.provider.ByteArrayAccess.*;
|
||||
|
||||
/**
|
||||
@ -205,8 +207,27 @@ abstract class SHA5 extends DigestBase {
|
||||
* "old" NIST Secure Hash Algorithm.
|
||||
*/
|
||||
final void implCompress(byte[] buf, int ofs) {
|
||||
b2lBig128(buf, ofs, W);
|
||||
implCompressCheck(buf, ofs);
|
||||
implCompress0(buf, ofs);
|
||||
}
|
||||
|
||||
private void implCompressCheck(byte[] buf, int ofs) {
|
||||
Objects.requireNonNull(buf);
|
||||
|
||||
// The checks performed by the method 'b2iBig128'
|
||||
// are sufficient for the case when the method
|
||||
// 'implCompressImpl' is replaced with a compiler
|
||||
// intrinsic.
|
||||
b2lBig128(buf, ofs, W);
|
||||
}
|
||||
|
||||
// The method 'implCompressImpl' seems not to use its parameters.
|
||||
// The method can, however, be replaced with a compiler intrinsic
|
||||
// that operates directly on the array 'buf' (starting from
|
||||
// offset 'ofs') and not on array 'W', therefore 'buf' and 'ofs'
|
||||
// must be passed as parameter to the method.
|
||||
@HotSpotIntrinsicCandidate
|
||||
private final void implCompress0(byte[] buf, int ofs) {
|
||||
// The first 16 longs are from the byte stream, compute the rest of
|
||||
// the W[]'s
|
||||
for (int t = 16; t < ITERATION; t++) {
|
||||
|
@ -556,6 +556,48 @@ JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused);
|
||||
JNIEXPORT jboolean JNICALL
|
||||
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
|
||||
*/
|
||||
|
177
jdk/src/java.base/share/native/libjava/Image.c
Normal file
177
jdk/src/java.base/share/native/libjava/Image.c
Normal file
@ -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;
|
||||
}
|
@ -43,8 +43,8 @@ Java_java_util_zip_CRC32_update(JNIEnv *env, jclass cls, jint crc, jint b)
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_java_util_zip_CRC32_updateBytes(JNIEnv *env, jclass cls, jint crc,
|
||||
jarray b, jint off, jint len)
|
||||
Java_java_util_zip_CRC32_updateBytes0(JNIEnv *env, jclass cls, jint crc,
|
||||
jarray b, jint off, jint len)
|
||||
{
|
||||
Bytef *buf = (*env)->GetPrimitiveArrayCritical(env, b, 0);
|
||||
if (buf) {
|
||||
@ -61,8 +61,8 @@ ZIP_CRC32(jint crc, const jbyte *buf, jint len)
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_java_util_zip_CRC32_updateByteBuffer(JNIEnv *env, jclass cls, jint crc,
|
||||
jlong address, jint off, jint len)
|
||||
Java_java_util_zip_CRC32_updateByteBuffer0(JNIEnv *env, jclass cls, jint crc,
|
||||
jlong address, jint off, jint len)
|
||||
{
|
||||
Bytef *buf = (Bytef *)jlong_to_ptr(address);
|
||||
if (buf) {
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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, '/');
|
||||
}
|
||||
}
|
@ -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 @@ class JImageTask {
|
||||
|
||||
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 @@ class JImageTask {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
Path jimage = options.jimages.get(0).toPath();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
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 @@ class JImageTask {
|
||||
}
|
||||
|
||||
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 @@ class JImageTask {
|
||||
|
||||
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 @@ class JImageTask {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ class JImageTask {
|
||||
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 @@ class JImageTask {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ class JImageTask {
|
||||
}
|
||||
|
||||
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 @@ class JImageTask {
|
||||
|
||||
private boolean run() throws IOException, BadArgs {
|
||||
switch (options.task) {
|
||||
case RECREATE:
|
||||
recreate();
|
||||
break;
|
||||
case EXTRACT:
|
||||
iterate(null, this::extract);
|
||||
break;
|
||||
@ -473,11 +393,17 @@ class JImageTask {
|
||||
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 @@ class JImageTask {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
231
jdk/src/jdk.dev/share/classes/jdk/tools/jimage/TaskHelper.java
Normal file
231
jdk/src/jdk.dev/share/classes/jdk/tools/jimage/TaskHelper.java
Normal file
@ -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");
|
||||
}
|
||||
|
||||
}
|
@ -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 @@ error.prefix=Error:
|
||||
warn.prefix=Warning:
|
||||
|
||||
main.opt.dir=\
|
||||
\ --dir Target directory for create/expand
|
||||
\ --dir Target directory for extract/recreate
|
||||
|
||||
main.opt.verbose=\
|
||||
\ --verbose Verbose listing
|
||||
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.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.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.read.file=cannot read file: {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}
|
||||
|
@ -394,7 +394,7 @@ final class JrtClassPathEntry extends ClassPathEntry {
|
||||
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) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user