This commit is contained in:
Alejandro Murillo 2015-07-09 22:46:18 -07:00
commit 867e029083
112 changed files with 6618 additions and 2662 deletions

View File

@ -144,7 +144,6 @@ $(eval $(call SetupJavaCompilation,BUILD_INTERIM_JIMAGE, \
SETUP := GENERATE_OLDBYTECODE, \ SETUP := GENERATE_OLDBYTECODE, \
SRC := $(JDK_TOPDIR)/src/java.base/share/classes, \ SRC := $(JDK_TOPDIR)/src/java.base/share/classes, \
INCLUDES := $(JIMAGE_PKGS), \ INCLUDES := $(JIMAGE_PKGS), \
EXCLUDES := jdk/internal/jimage/concurrent, \
BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_jimage_classes)) BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_jimage_classes))
# Because of the explicit INCLUDES in the compilation setup above, the service provider # Because of the explicit INCLUDES in the compilation setup above, the service provider

View File

@ -239,6 +239,16 @@ SUNWprivate_1.1 {
Java_java_util_TimeZone_getSystemTimeZoneID; Java_java_util_TimeZone_getSystemTimeZoneID;
Java_java_util_TimeZone_getSystemGMTOffsetID; Java_java_util_TimeZone_getSystemGMTOffsetID;
Java_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8; 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_toStderr;
Java_sun_misc_MessageUtils_toStdout; Java_sun_misc_MessageUtils_toStdout;
Java_sun_misc_NativeSignalHandler_handle0; Java_sun_misc_NativeSignalHandler_handle0;
@ -281,9 +291,6 @@ SUNWprivate_1.1 {
Java_sun_misc_VMSupport_initAgentProperties; Java_sun_misc_VMSupport_initAgentProperties;
Java_sun_misc_VMSupport_getVMTemporaryDirectory; Java_sun_misc_VMSupport_getVMTemporaryDirectory;
Java_jdk_internal_jimage_concurrent_ConcurrentPReader_initIDs;
Java_jdk_internal_jimage_concurrent_ConcurrentPReader_pread;
# ZipFile.c needs this one # ZipFile.c needs this one
throwFileNotFoundException; throwFileNotFoundException;
# zip_util.c needs this one # zip_util.c needs this one

View File

@ -32,8 +32,8 @@ SUNWprivate_1.1 {
Java_java_util_zip_Adler32_updateBytes; Java_java_util_zip_Adler32_updateBytes;
Java_java_util_zip_Adler32_updateByteBuffer; Java_java_util_zip_Adler32_updateByteBuffer;
Java_java_util_zip_CRC32_update; Java_java_util_zip_CRC32_update;
Java_java_util_zip_CRC32_updateBytes; Java_java_util_zip_CRC32_updateBytes0;
Java_java_util_zip_CRC32_updateByteBuffer; Java_java_util_zip_CRC32_updateByteBuffer0;
Java_java_util_zip_Deflater_deflateBytes; Java_java_util_zip_Deflater_deflateBytes;
Java_java_util_zip_Deflater_end; Java_java_util_zip_Deflater_end;
Java_java_util_zip_Deflater_getAdler; Java_java_util_zip_Deflater_getAdler;

View File

@ -26,8 +26,6 @@
package build.tools.module; package build.tools.module;
import jdk.internal.jimage.Archive; import jdk.internal.jimage.Archive;
import jdk.internal.jimage.ImageFile;
import jdk.internal.jimage.ImageModules;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
@ -35,13 +33,11 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.UncheckedIOException;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.InvalidPathException; import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@ -52,6 +48,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import jdk.internal.jimage.ImageFileCreator;
/** /**
* A tool for building a runtime image. * A tool for building a runtime image.
@ -84,11 +81,23 @@ class ImageBuilder {
boolean showUsage; 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 boolean hasArg;
final String[] aliases; 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.hasArg = hasArg;
this.description = description;
this.processing = processing;
this.aliases = aliases; this.aliases = aliases;
} }
boolean isHidden() { boolean isHidden() {
@ -107,8 +116,12 @@ class ImageBuilder {
boolean ignoreRest() { boolean ignoreRest() {
return false; return false;
} }
abstract void process(ImageBuilder task, String opt, String arg) throws BadArgs; void process(ImageBuilder task, String opt, String arg) throws BadArgs {
abstract String description(); processing.process(task, opt, arg);
}
String description() {
return description;
}
} }
private static Path CWD = Paths.get(""); private static Path CWD = Paths.get("");
@ -133,64 +146,44 @@ class ImageBuilder {
} }
static Option[] recognizedOptions = { static Option[] recognizedOptions = {
new Option(true, "--cmds") { new Option(true, "Location of native commands", (task, opt, arg) -> {
void process(ImageBuilder task, String opt, String arg) throws BadArgs { task.options.cmds = splitPath(arg, File.pathSeparator);
task.options.cmds = splitPath(arg, File.pathSeparator); }, "--cmds"),
} new Option(true, "Location of config files", (task, opt, arg) -> {
String description() { return "Location of native commands"; } task.options.configs = splitPath(arg, File.pathSeparator);
}, }, "--configs"),
new Option(true, "--configs") { new Option(false, "Print this usage message", (task, opt, arg) -> {
void process(ImageBuilder task, String opt, String arg) throws BadArgs { task.options.help = true;
task.options.configs = splitPath(arg, File.pathSeparator); }, "--help"),
} new Option(true, "Location of module classes files", (task, opt, arg) -> {
String description() { return "Location of config files"; } task.options.classes = splitPath(arg, File.pathSeparator);
}, }, "--classes"),
new Option(false, "--help") { new Option(true, "Location of native libraries", (task, opt, arg) -> {
void process(ImageBuilder task, String opt, String arg) { task.options.libs = splitPath(arg, File.pathSeparator);
task.options.help = true; }, "--libs"),
} new Option(true, "Comma separated list of module names",
String description() { return "Print this usage message"; } (task, opt, arg) -> {
}, for (String mn : arg.split(",")) {
new Option(true, "--classes") { if (mn.isEmpty()) {
void process(ImageBuilder task, String opt, String arg) throws BadArgs { throw new BadArgs("Module not found", mn);
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);
} }
task.options.mods.add(mn);
} }
String description() { return "Comma separated list of module names"; } }, "--mods"),
}, new Option(true, "Location of the output path", (task, opt, arg) -> {
new Option(true, "--output") { Path path = Paths.get(arg);
void process(ImageBuilder task, String opt, String arg) throws BadArgs { task.options.output = path;
Path path = Paths.get(arg); }, "--output"),
task.options.output = path; 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"; } }, "--endian")
},
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}"; }
}
}; };
private final Options options = new Options(); private final Options options = new Options();
@ -370,28 +363,35 @@ class ImageBuilder {
final Set<String> bootModules; final Set<String> bootModules;
final Set<String> extModules; final Set<String> extModules;
final Set<String> appModules; final Set<String> appModules;
final ImageModules imf;
ImageFileHelper(Collection<String> modules) throws IOException { ImageFileHelper(Collection<String> modules) throws IOException {
this.modules = modules; this.modules = modules;
this.bootModules = modulesFor(BOOT_MODULES).stream() this.bootModules = modulesFor(BOOT_MODULES).stream()
.filter(modules::contains) .filter(modules::contains)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
this.extModules = modulesFor(EXT_MODULES).stream() this.extModules = modulesFor(EXT_MODULES).stream()
.filter(modules::contains) .filter(modules::contains)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
this.appModules = modules.stream() 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()); .collect(Collectors.toSet());
this.imf = new ImageModules(bootModules, extModules, appModules);
} }
void createModularImage(Path output) throws IOException { void createModularImage(Path output) throws IOException {
Set<Archive> archives = modules.stream() Set<Archive> bootArchives = bootModules.stream()
.map(this::toModuleArchive) .map(this::toModuleArchive)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
ImageFile.create(output, archives, imf, options.endian); 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) { ModuleArchive toModuleArchive(String mn) {

View File

@ -26,15 +26,17 @@
package build.tools.module; package build.tools.module;
import jdk.internal.jimage.Archive; import jdk.internal.jimage.Archive;
import jdk.internal.jimage.Resource;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; 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. * An Archive backed by an exploded representation on disk.
@ -46,6 +48,8 @@ public class ModuleArchive implements Archive {
private final Path configs; private final Path configs;
private final String moduleName; private final String moduleName;
private final List<InputStream> opened = new ArrayList<>();
public ModuleArchive(String moduleName, Path classes, Path cmds, public ModuleArchive(String moduleName, Path classes, Path cmds,
Path libs, Path configs) { Path libs, Path configs) {
this.moduleName = moduleName; this.moduleName = moduleName;
@ -61,183 +65,119 @@ public class ModuleArchive implements Archive {
} }
@Override @Override
public void visitResources(Consumer<Resource> consumer) { public void open() throws IOException {
if (classes == null) // NOOP
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
} }
@Override @Override
public void visitEntries(Consumer<Entry> consumer) { public void close() throws IOException {
try{ IOException e = null;
if (classes != null) for (InputStream stream : opened) {
Files.walk(classes) try {
.sorted() stream.close();
.filter(p -> !Files.isDirectory(p) } catch (IOException ex) {
&& !classes.relativize(p).toString().startsWith("_the.") if (e == null) {
&& !classes.relativize(p).toString().equals("javac_state")) e = ex;
.map(p -> toEntry(p, classes, Section.CLASSES)) } else {
.forEach(consumer::accept); e.addSuppressed(ex);
if (cmds != null) }
Files.walk(cmds) }
}
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)) .filter(p -> !Files.isDirectory(p))
.map(p -> toEntry(p, cmds, Section.CMDS)) .map(p -> toEntry(p, configs, EntryType.CONFIG))
.forEach(consumer::accept); .collect(Collectors.toList()));
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);
} catch (IOException ioe) { } catch (IOException ioe) {
throw new UncheckedIOException(ioe); throw new UncheckedIOException(ioe);
} }
return entries.stream();
} }
private static class FileEntry implements Entry { private class FileEntry extends Entry {
private final String name;
private final InputStream is;
private final boolean isDirectory; private final boolean isDirectory;
private final Section section; private final long size;
FileEntry(String name, InputStream is, private final Path entryPath;
boolean isDirectory, Section section) { FileEntry(Path entryPath, String path, EntryType type,
this.name = name; boolean isDirectory, long size) {
this.is = is; super(ModuleArchive.this, path, path, type);
this.entryPath = entryPath;
this.isDirectory = isDirectory; this.isDirectory = isDirectory;
this.section = section; this.size = size;
}
public String getName() {
return name;
}
public Section getSection() {
return section;
}
public InputStream getInputStream() {
return is;
} }
public boolean isDirectory() { public boolean isDirectory() {
return 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 { try {
return new FileEntry(basePath.relativize(entryPath).toString().replace('\\', '/'), String path = basePath.relativize(entryPath).toString().replace('\\', '/');
Files.newInputStream(entryPath), false, return new FileEntry(entryPath, path, section,
section); false, Files.size(entryPath));
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(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";
}
}
}
} }

View File

@ -38,6 +38,9 @@ package com.sun.crypto.provider;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* Rijndael --pronounced Reindaal-- is a symmetric cipher with a 128-bit * 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. * Encrypt exactly one block of plaintext.
*/ */
void encryptBlock(byte[] in, int inOffset, 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 keyOffset = 0;
int t0 = ((in[inOffset++] ) << 24 | int t0 = ((in[inOffset++] ) << 24 |
@ -412,12 +424,20 @@ final class AESCrypt extends SymmetricCipher implements AESConstants
out[outOffset ] = (byte)(S[(t2 ) & 0xFF] ^ (tt )); out[outOffset ] = (byte)(S[(t2 ) & 0xFF] ^ (tt ));
} }
/** /**
* Decrypt exactly one block of plaintext. * Decrypt exactly one block of plaintext.
*/ */
void decryptBlock(byte[] in, int inOffset, 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 keyOffset = 4;
int t0 = ((in[inOffset++] ) << 24 | int t0 = ((in[inOffset++] ) << 24 |
@ -572,6 +592,25 @@ final class AESCrypt extends SymmetricCipher implements AESConstants
out[outOffset ] = (byte)(Si[(a0 ) & 0xFF] ^ (t1 )); 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. * Expand a user-supplied key material into a session key.

View File

@ -27,6 +27,9 @@ package com.sun.crypto.provider;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.ProviderException; 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 * @return the length of the encrypted data
*/ */
int encrypt(byte[] plain, int plainOffset, int plainLen, 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; int endIndex = plainOffset + plainLen;
for (; plainOffset < endIndex; for (; plainOffset < endIndex;
plainOffset+=blockSize, cipherOffset += blockSize) { plainOffset += blockSize, cipherOffset += blockSize) {
for (int i = 0; i < blockSize; i++) { for (int i = 0; i < blockSize; i++) {
k[i] = (byte)(plain[i + plainOffset] ^ r[i]); k[i] = (byte)(plain[i + plainOffset] ^ r[i]);
} }
@ -179,11 +189,17 @@ class CipherBlockChaining extends FeedbackCipher {
* @return the length of the decrypted data * @return the length of the decrypted data
*/ */
int decrypt(byte[] cipher, int cipherOffset, int cipherLen, 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; int endIndex = cipherOffset + cipherLen;
for (; cipherOffset < endIndex; for (; cipherOffset < endIndex;
@ -196,4 +212,27 @@ class CipherBlockChaining extends FeedbackCipher {
} }
return cipherLen; 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);
}
}
} }

View File

@ -31,6 +31,8 @@ package com.sun.crypto.provider;
import java.security.ProviderException; import java.security.ProviderException;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* This class represents the GHASH function defined in NIST 800-38D * 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. * 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 * the hotspot signature. This method and methods called by it, cannot
* throw exceptions or allocate arrays as it will breaking intrinsics * 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) { private static void processBlocks(byte[] data, int inOfs, int blocks, long[] st, long[] subH) {
int offset = inOfs; int offset = inOfs;
while (blocks > 0) { while (blocks > 0) {

View File

@ -25,6 +25,8 @@
package java.lang; package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The Boolean class wraps a value of the primitive type * The Boolean class wraps a value of the primitive type
* {@code boolean} in an object. An object of 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. * @return the primitive {@code boolean} value of this object.
*/ */
@HotSpotIntrinsicCandidate
public boolean booleanValue() { public boolean booleanValue() {
return value; return value;
} }
@ -146,6 +149,7 @@ public final class Boolean implements java.io.Serializable,
* @return a {@code Boolean} instance representing {@code b}. * @return a {@code Boolean} instance representing {@code b}.
* @since 1.4 * @since 1.4
*/ */
@HotSpotIntrinsicCandidate
public static Boolean valueOf(boolean b) { public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE); return (b ? TRUE : FALSE);
} }

View File

@ -25,6 +25,8 @@
package java.lang; package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* *
* The {@code Byte} class wraps a value of primitive type {@code byte} * 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}. * @return a {@code Byte} instance representing {@code b}.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static Byte valueOf(byte b) { public static Byte valueOf(byte b) {
final int offset = 128; final int offset = 128;
return ByteCache.cache[(int)b + offset]; 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 * Returns the value of this {@code Byte} as a
* {@code byte}. * {@code byte}.
*/ */
@HotSpotIntrinsicCandidate
public byte byteValue() { public byte byteValue() {
return value; return value;
} }

View File

@ -30,6 +30,8 @@ import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The {@code Character} class wraps a value of the primitive * The {@code Character} class wraps a value of the primitive
* type {@code char} in an object. An object of type * 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>. * @return a <tt>Character</tt> instance representing <tt>c</tt>.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static Character valueOf(char c) { public static Character valueOf(char c) {
if (c <= 127) { // must cache if (c <= 127) { // must cache
return CharacterCache.cache[(int)c]; 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 * @return the primitive {@code char} value represented by
* this object. * this object.
*/ */
@HotSpotIntrinsicCandidate
public char charValue() { public char charValue() {
return value; return value;
} }
@ -7181,6 +7185,7 @@ class Character implements java.io.Serializable, Comparable<Character> {
* the bytes in the specified <tt>char</tt> value. * the bytes in the specified <tt>char</tt> value.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static char reverseBytes(char ch) { public static char reverseBytes(char ch) {
return (char) (((ch & 0xFF00) >> 8) | (ch << 8)); return (char) (((ch & 0xFF00) >> 8) | (ch << 8));
} }

View File

@ -56,6 +56,7 @@ import java.util.HashMap;
import java.util.Objects; import java.util.Objects;
import java.util.StringJoiner; import java.util.StringJoiner;
import sun.misc.Unsafe; import sun.misc.Unsafe;
import jdk.internal.HotSpotIntrinsicCandidate;
import sun.reflect.CallerSensitive; import sun.reflect.CallerSensitive;
import sun.reflect.ConstantPool; import sun.reflect.ConstantPool;
import sun.reflect.Reflection; import sun.reflect.Reflection;
@ -502,6 +503,7 @@ public final class Class<T> implements java.io.Serializable,
* *
* @since 1.1 * @since 1.1
*/ */
@HotSpotIntrinsicCandidate
public native boolean isInstance(Object obj); public native boolean isInstance(Object obj);
@ -529,6 +531,7 @@ public final class Class<T> implements java.io.Serializable,
* null. * null.
* @since 1.1 * @since 1.1
*/ */
@HotSpotIntrinsicCandidate
public native boolean isAssignableFrom(Class<?> cls); 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; * @return {@code true} if this object represents an interface;
* {@code false} otherwise. * {@code false} otherwise.
*/ */
@HotSpotIntrinsicCandidate
public native boolean isInterface(); public native boolean isInterface();
@ -549,6 +553,7 @@ public final class Class<T> implements java.io.Serializable,
* {@code false} otherwise. * {@code false} otherwise.
* @since 1.1 * @since 1.1
*/ */
@HotSpotIntrinsicCandidate
public native boolean isArray(); public native boolean isArray();
@ -580,6 +585,7 @@ public final class Class<T> implements java.io.Serializable,
* @see java.lang.Void#TYPE * @see java.lang.Void#TYPE
* @since 1.1 * @since 1.1
*/ */
@HotSpotIntrinsicCandidate
public native boolean isPrimitive(); 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 * @return the direct superclass of the class represented by this object
*/ */
@HotSpotIntrinsicCandidate
public native Class<? super T> getSuperclass(); public native Class<? super T> getSuperclass();
@ -984,6 +991,7 @@ public final class Class<T> implements java.io.Serializable,
* @see java.lang.reflect.Modifier * @see java.lang.reflect.Modifier
* @since 1.1 * @since 1.1
*/ */
@HotSpotIntrinsicCandidate
public native int getModifiers(); public native int getModifiers();
@ -3382,6 +3390,7 @@ public final class Class<T> implements java.io.Serializable,
* @since 1.5 * @since 1.5
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@HotSpotIntrinsicCandidate
public T cast(Object obj) { public T cast(Object obj) {
if (obj != null && !isInstance(obj)) if (obj != null && !isInstance(obj))
throw new ClassCastException(cannotCastMsg(obj)); throw new ClassCastException(cannotCastMsg(obj));

View File

@ -27,6 +27,7 @@ package java.lang;
import sun.misc.FloatingDecimal; import sun.misc.FloatingDecimal;
import sun.misc.DoubleConsts; import sun.misc.DoubleConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The {@code Double} class wraps a value of the primitive type * 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}. * @return a {@code Double} instance representing {@code d}.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static Double valueOf(double d) { public static Double valueOf(double d) {
return new 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 * @return the {@code double} value represented by this object
*/ */
@HotSpotIntrinsicCandidate
public double doubleValue() { public double doubleValue() {
return value; return value;
} }
@ -831,6 +834,7 @@ public final class Double extends Number implements Comparable<Double> {
* @param value a {@code double} precision floating-point number. * @param value a {@code double} precision floating-point number.
* @return the bits that represent the floating-point number. * @return the bits that represent the floating-point number.
*/ */
@HotSpotIntrinsicCandidate
public static long doubleToLongBits(double value) { public static long doubleToLongBits(double value) {
if (!isNaN(value)) { if (!isNaN(value)) {
return doubleToRawLongBits(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. * @return the bits that represent the floating-point number.
* @since 1.3 * @since 1.3
*/ */
@HotSpotIntrinsicCandidate
public static native long doubleToRawLongBits(double value); 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 * @return the {@code double} floating-point value with the same
* bit pattern. * bit pattern.
*/ */
@HotSpotIntrinsicCandidate
public static native double longBitsToDouble(long bits); public static native double longBitsToDouble(long bits);
/** /**

View File

@ -28,6 +28,7 @@ package java.lang;
import sun.misc.FloatingDecimal; import sun.misc.FloatingDecimal;
import sun.misc.FloatConsts; import sun.misc.FloatConsts;
import sun.misc.DoubleConsts; import sun.misc.DoubleConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The {@code Float} class wraps a value of primitive type * 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}. * @return a {@code Float} instance representing {@code f}.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static Float valueOf(float f) { public static Float valueOf(float f) {
return new 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 * @return the {@code float} value represented by this object
*/ */
@HotSpotIntrinsicCandidate
public float floatValue() { public float floatValue() {
return value; return value;
} }
@ -740,6 +743,7 @@ public final class Float extends Number implements Comparable<Float> {
* @param value a floating-point number. * @param value a floating-point number.
* @return the bits that represent the floating-point number. * @return the bits that represent the floating-point number.
*/ */
@HotSpotIntrinsicCandidate
public static int floatToIntBits(float value) { public static int floatToIntBits(float value) {
if (!isNaN(value)) { if (!isNaN(value)) {
return floatToRawIntBits(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. * @return the bits that represent the floating-point number.
* @since 1.3 * @since 1.3
*/ */
@HotSpotIntrinsicCandidate
public static native int floatToRawIntBits(float value); 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 * @return the {@code float} floating-point value with the same bit
* pattern. * pattern.
*/ */
@HotSpotIntrinsicCandidate
public static native float intBitsToFloat(int bits); public static native float intBitsToFloat(int bits);
/** /**

View File

@ -27,6 +27,7 @@ package java.lang;
import java.lang.annotation.Native; import java.lang.annotation.Native;
import java.util.Objects; import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The {@code Integer} class wraps a value of the primitive type * 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. * @param i an integer to be converted.
* @return a string representation of the argument in base&nbsp;10. * @return a string representation of the argument in base&nbsp;10.
*/ */
@HotSpotIntrinsicCandidate
public static String toString(int i) { public static String toString(int i) {
if (i == Integer.MIN_VALUE) if (i == Integer.MIN_VALUE)
return "-2147483648"; return "-2147483648";
@ -972,6 +974,7 @@ public final class Integer extends Number implements Comparable<Integer> {
* @return an {@code Integer} instance representing {@code i}. * @return an {@code Integer} instance representing {@code i}.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) { public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high) if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)]; 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 * Returns the value of this {@code Integer} as an
* {@code int}. * {@code int}.
*/ */
@HotSpotIntrinsicCandidate
public int intValue() { public int intValue() {
return value; return value;
} }
@ -1538,6 +1542,7 @@ public final class Integer extends Number implements Comparable<Integer> {
* is equal to zero. * is equal to zero.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static int numberOfLeadingZeros(int i) { public static int numberOfLeadingZeros(int i) {
// HD, Figure 5-6 // HD, Figure 5-6
if (i == 0) if (i == 0)
@ -1565,6 +1570,7 @@ public final class Integer extends Number implements Comparable<Integer> {
* to zero. * to zero.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static int numberOfTrailingZeros(int i) { public static int numberOfTrailingZeros(int i) {
// HD, Figure 5-14 // HD, Figure 5-14
int y; int y;
@ -1587,6 +1593,7 @@ public final class Integer extends Number implements Comparable<Integer> {
* representation of the specified {@code int} value. * representation of the specified {@code int} value.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static int bitCount(int i) { public static int bitCount(int i) {
// HD, Figure 5-2 // HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555); i = i - ((i >>> 1) & 0x55555555);
@ -1688,6 +1695,7 @@ public final class Integer extends Number implements Comparable<Integer> {
* {@code int} value. * {@code int} value.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static int reverseBytes(int i) { public static int reverseBytes(int i) {
return ((i >>> 24) ) | return ((i >>> 24) ) |
((i >> 8) & 0xFF00) | ((i >> 8) & 0xFF00) |

View File

@ -28,6 +28,7 @@ package java.lang;
import java.lang.annotation.Native; import java.lang.annotation.Native;
import java.math.*; import java.math.*;
import java.util.Objects; 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}. * @return a {@code Long} instance representing {@code l}.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static Long valueOf(long l) { public static Long valueOf(long l) {
final int offset = 128; final int offset = 128;
if (l >= -128 && l <= 127) { // will cache 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 * Returns the value of this {@code Long} as a
* {@code long} value. * {@code long} value.
*/ */
@HotSpotIntrinsicCandidate
public long longValue() { public long longValue() {
return value; return value;
} }
@ -1655,6 +1658,7 @@ public final class Long extends Number implements Comparable<Long> {
* is equal to zero. * is equal to zero.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static int numberOfLeadingZeros(long i) { public static int numberOfLeadingZeros(long i) {
// HD, Figure 5-6 // HD, Figure 5-6
if (i == 0) if (i == 0)
@ -1684,6 +1688,7 @@ public final class Long extends Number implements Comparable<Long> {
* to zero. * to zero.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static int numberOfTrailingZeros(long i) { public static int numberOfTrailingZeros(long i) {
// HD, Figure 5-14 // HD, Figure 5-14
int x, y; int x, y;
@ -1707,6 +1712,7 @@ public final class Long extends Number implements Comparable<Long> {
* representation of the specified {@code long} value. * representation of the specified {@code long} value.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static int bitCount(long i) { public static int bitCount(long i) {
// HD, Figure 5-2 // HD, Figure 5-2
i = i - ((i >>> 1) & 0x5555555555555555L); i = i - ((i >>> 1) & 0x5555555555555555L);
@ -1810,6 +1816,7 @@ public final class Long extends Number implements Comparable<Long> {
* {@code long} value. * {@code long} value.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static long reverseBytes(long i) { public static long reverseBytes(long i) {
i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL; i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;
return (i << 48) | ((i & 0xffff0000L) << 16) | return (i << 48) | ((i & 0xffff0000L) << 16) |

View File

@ -24,10 +24,11 @@
*/ */
package java.lang; package java.lang;
import java.util.Random;
import java.util.Random;
import sun.misc.FloatConsts; import sun.misc.FloatConsts;
import sun.misc.DoubleConsts; import sun.misc.DoubleConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The class {@code Math} contains methods for performing basic * The class {@code Math} contains methods for performing basic
@ -147,6 +148,7 @@ public final class Math {
* @param a an angle, in radians. * @param a an angle, in radians.
* @return the sine of the argument. * @return the sine of the argument.
*/ */
@HotSpotIntrinsicCandidate
public static double sin(double a) { public static double sin(double a) {
return StrictMath.sin(a); // default impl. delegates to StrictMath return StrictMath.sin(a); // default impl. delegates to StrictMath
} }
@ -162,6 +164,7 @@ public final class Math {
* @param a an angle, in radians. * @param a an angle, in radians.
* @return the cosine of the argument. * @return the cosine of the argument.
*/ */
@HotSpotIntrinsicCandidate
public static double cos(double a) { public static double cos(double a) {
return StrictMath.cos(a); // default impl. delegates to StrictMath return StrictMath.cos(a); // default impl. delegates to StrictMath
} }
@ -179,6 +182,7 @@ public final class Math {
* @param a an angle, in radians. * @param a an angle, in radians.
* @return the tangent of the argument. * @return the tangent of the argument.
*/ */
@HotSpotIntrinsicCandidate
public static double tan(double a) { public static double tan(double a) {
return StrictMath.tan(a); // default impl. delegates to StrictMath 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>, * @return the value <i>e</i><sup>{@code a}</sup>,
* where <i>e</i> is the base of the natural logarithms. * where <i>e</i> is the base of the natural logarithms.
*/ */
@HotSpotIntrinsicCandidate
public static double exp(double a) { public static double exp(double a) {
return StrictMath.exp(a); // default impl. delegates to StrictMath return StrictMath.exp(a); // default impl. delegates to StrictMath
} }
@ -301,6 +306,7 @@ public final class Math {
* @return the value ln&nbsp;{@code a}, the natural logarithm of * @return the value ln&nbsp;{@code a}, the natural logarithm of
* {@code a}. * {@code a}.
*/ */
@HotSpotIntrinsicCandidate
public static double log(double a) { public static double log(double a) {
return StrictMath.log(a); // default impl. delegates to StrictMath 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}. * @return the base 10 logarithm of {@code a}.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static double log10(double a) { public static double log10(double a) {
return StrictMath.log10(a); // default impl. delegates to StrictMath 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}. * @return the positive square root of {@code a}.
* If the argument is NaN or less than zero, the result is NaN. * If the argument is NaN or less than zero, the result is NaN.
*/ */
@HotSpotIntrinsicCandidate
public static double sqrt(double a) { public static double sqrt(double a) {
return StrictMath.sqrt(a); // default impl. delegates to StrictMath return StrictMath.sqrt(a); // default impl. delegates to StrictMath
// Note that hardware sqrt instructions // Note that hardware sqrt instructions
@ -525,6 +533,7 @@ public final class Math {
* in polar coordinates that corresponds to the point * in polar coordinates that corresponds to the point
* (<i>x</i>,&nbsp;<i>y</i>) in Cartesian coordinates. * (<i>x</i>,&nbsp;<i>y</i>) in Cartesian coordinates.
*/ */
@HotSpotIntrinsicCandidate
public static double atan2(double y, double x) { public static double atan2(double y, double x) {
return StrictMath.atan2(y, x); // default impl. delegates to StrictMath return StrictMath.atan2(y, x); // default impl. delegates to StrictMath
} }
@ -652,6 +661,7 @@ public final class Math {
* @param b the exponent. * @param b the exponent.
* @return the value {@code a}<sup>{@code b}</sup>. * @return the value {@code a}<sup>{@code b}</sup>.
*/ */
@HotSpotIntrinsicCandidate
public static double pow(double a, double b) { public static double pow(double a, double b) {
return StrictMath.pow(a, b); // default impl. delegates to StrictMath 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 * @throws ArithmeticException if the result overflows an int
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static int addExact(int x, int y) { public static int addExact(int x, int y) {
int r = x + y; int r = x + y;
// HD 2-12 Overflow iff both arguments have the opposite sign of the result // 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 * @throws ArithmeticException if the result overflows a long
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static long addExact(long x, long y) { public static long addExact(long x, long y) {
long r = x + y; long r = x + y;
// HD 2-12 Overflow iff both arguments have the opposite sign of the result // 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 * @throws ArithmeticException if the result overflows an int
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static int subtractExact(int x, int y) { public static int subtractExact(int x, int y) {
int r = x - y; int r = x - y;
// HD 2-12 Overflow iff the arguments have different signs and // 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 * @throws ArithmeticException if the result overflows a long
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static long subtractExact(long x, long y) { public static long subtractExact(long x, long y) {
long r = x - y; long r = x - y;
// HD 2-12 Overflow iff the arguments have different signs and // 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 * @throws ArithmeticException if the result overflows an int
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static int multiplyExact(int x, int y) { public static int multiplyExact(int x, int y) {
long r = (long)x * (long)y; long r = (long)x * (long)y;
if ((int)r != r) { if ((int)r != r) {
@ -902,6 +917,7 @@ public final class Math {
* @throws ArithmeticException if the result overflows a long * @throws ArithmeticException if the result overflows a long
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static long multiplyExact(long x, long y) { public static long multiplyExact(long x, long y) {
long r = x * y; long r = x * y;
long ax = Math.abs(x); long ax = Math.abs(x);
@ -927,6 +943,7 @@ public final class Math {
* @throws ArithmeticException if the result overflows an int * @throws ArithmeticException if the result overflows an int
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static int incrementExact(int a) { public static int incrementExact(int a) {
if (a == Integer.MAX_VALUE) { if (a == Integer.MAX_VALUE) {
throw new ArithmeticException("integer overflow"); throw new ArithmeticException("integer overflow");
@ -944,6 +961,7 @@ public final class Math {
* @throws ArithmeticException if the result overflows a long * @throws ArithmeticException if the result overflows a long
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static long incrementExact(long a) { public static long incrementExact(long a) {
if (a == Long.MAX_VALUE) { if (a == Long.MAX_VALUE) {
throw new ArithmeticException("long overflow"); throw new ArithmeticException("long overflow");
@ -961,6 +979,7 @@ public final class Math {
* @throws ArithmeticException if the result overflows an int * @throws ArithmeticException if the result overflows an int
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static int decrementExact(int a) { public static int decrementExact(int a) {
if (a == Integer.MIN_VALUE) { if (a == Integer.MIN_VALUE) {
throw new ArithmeticException("integer overflow"); throw new ArithmeticException("integer overflow");
@ -978,6 +997,7 @@ public final class Math {
* @throws ArithmeticException if the result overflows a long * @throws ArithmeticException if the result overflows a long
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static long decrementExact(long a) { public static long decrementExact(long a) {
if (a == Long.MIN_VALUE) { if (a == Long.MIN_VALUE) {
throw new ArithmeticException("long overflow"); throw new ArithmeticException("long overflow");
@ -995,6 +1015,7 @@ public final class Math {
* @throws ArithmeticException if the result overflows an int * @throws ArithmeticException if the result overflows an int
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static int negateExact(int a) { public static int negateExact(int a) {
if (a == Integer.MIN_VALUE) { if (a == Integer.MIN_VALUE) {
throw new ArithmeticException("integer overflow"); throw new ArithmeticException("integer overflow");
@ -1012,6 +1033,7 @@ public final class Math {
* @throws ArithmeticException if the result overflows a long * @throws ArithmeticException if the result overflows a long
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public static long negateExact(long a) { public static long negateExact(long a) {
if (a == Long.MIN_VALUE) { if (a == Long.MIN_VALUE) {
throw new ArithmeticException("long overflow"); throw new ArithmeticException("long overflow");
@ -1256,6 +1278,7 @@ public final class Math {
* @param a the argument whose absolute value is to be determined * @param a the argument whose absolute value is to be determined
* @return the absolute value of the argument. * @return the absolute value of the argument.
*/ */
@HotSpotIntrinsicCandidate
public static double abs(double a) { public static double abs(double a) {
return (a <= 0.0D) ? 0.0D - a : a; return (a <= 0.0D) ? 0.0D - a : a;
} }
@ -1270,6 +1293,7 @@ public final class Math {
* @param b another argument. * @param b another argument.
* @return the larger of {@code a} and {@code b}. * @return the larger of {@code a} and {@code b}.
*/ */
@HotSpotIntrinsicCandidate
public static int max(int a, int b) { public static int max(int a, int b) {
return (a >= b) ? a : b; return (a >= b) ? a : b;
} }
@ -1354,6 +1378,7 @@ public final class Math {
* @param b another argument. * @param b another argument.
* @return the smaller of {@code a} and {@code b}. * @return the smaller of {@code a} and {@code b}.
*/ */
@HotSpotIntrinsicCandidate
public static int min(int a, int b) { public static int min(int a, int b) {
return (a <= b) ? a : b; return (a <= b) ? a : b;
} }

View File

@ -25,6 +25,8 @@
package java.lang; package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* Class {@code Object} is the root of the class hierarchy. * Class {@code Object} is the root of the class hierarchy.
* Every class has {@code Object} as a superclass. All objects, * Every class has {@code Object} as a superclass. All objects,
@ -44,6 +46,7 @@ public class Object {
/** /**
* Constructs a new object. * Constructs a new object.
*/ */
@HotSpotIntrinsicCandidate
public Object() {} public Object() {}
/** /**
@ -65,6 +68,7 @@ public class Object {
* class of this object. * class of this object.
* @jls 15.8.2 Class Literals * @jls 15.8.2 Class Literals
*/ */
@HotSpotIntrinsicCandidate
public final native Class<?> getClass(); public final native Class<?> getClass();
/** /**
@ -101,6 +105,7 @@ public class Object {
* @see java.lang.Object#equals(java.lang.Object) * @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode * @see java.lang.System#identityHashCode
*/ */
@HotSpotIntrinsicCandidate
public native int hashCode(); public native int hashCode();
/** /**
@ -213,6 +218,7 @@ public class Object {
* be cloned. * be cloned.
* @see java.lang.Cloneable * @see java.lang.Cloneable
*/ */
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException; protected native Object clone() throws CloneNotSupportedException;
/** /**

View File

@ -25,6 +25,8 @@
package java.lang; package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The {@code Short} class wraps a value of primitive type {@code * The {@code Short} class wraps a value of primitive type {@code
* short} in an object. An object of type {@code Short} contains a * 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}. * @return a {@code Short} instance representing {@code s}.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static Short valueOf(short s) { public static Short valueOf(short s) {
final int offset = 128; final int offset = 128;
int sAsInt = s; 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 * Returns the value of this {@code Short} as a
* {@code short}. * {@code short}.
*/ */
@HotSpotIntrinsicCandidate
public short shortValue() { public short shortValue() {
return value; return value;
} }
@ -487,6 +491,7 @@ public final class Short extends Number implements Comparable<Short> {
* the bytes in the specified {@code short} value. * the bytes in the specified {@code short} value.
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static short reverseBytes(short i) { public static short reverseBytes(short i) {
return (short) (((i & 0xFF00) >> 8) | (i << 8)); return (short) (((i & 0xFF00) >> 8) | (i << 8));
} }

View File

@ -24,8 +24,10 @@
*/ */
package java.lang; package java.lang;
import java.util.Random; import java.util.Random;
import sun.misc.DoubleConsts; import sun.misc.DoubleConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The class {@code StrictMath} contains methods for performing basic * The class {@code StrictMath} contains methods for performing basic
@ -243,7 +245,6 @@ public final class StrictMath {
*/ */
public static native double log(double a); public static native double log(double a);
/** /**
* Returns the base 10 logarithm of a {@code double} value. * Returns the base 10 logarithm of a {@code double} value.
* Special cases: * Special cases:
@ -280,6 +281,7 @@ public final class StrictMath {
* @param a a value. * @param a a value.
* @return the positive square root of {@code a}. * @return the positive square root of {@code a}.
*/ */
@HotSpotIntrinsicCandidate
public static native double sqrt(double a); public static native double sqrt(double a);
/** /**
@ -521,7 +523,6 @@ public final class StrictMath {
*/ */
public static native double atan2(double y, double x); public static native double atan2(double y, double x);
/** /**
* Returns the value of the first argument raised to the power of the * Returns the value of the first argument raised to the power of the
* second argument. Special cases: * second argument. Special cases:
@ -1009,6 +1010,7 @@ public final class StrictMath {
* @param b another argument. * @param b another argument.
* @return the larger of {@code a} and {@code b}. * @return the larger of {@code a} and {@code b}.
*/ */
@HotSpotIntrinsicCandidate
public static int max(int a, int b) { public static int max(int a, int b) {
return Math.max(a, b); return Math.max(a, b);
} }
@ -1073,6 +1075,7 @@ public final class StrictMath {
* @param b another argument. * @param b another argument.
* @return the smaller of {@code a} and {@code b}. * @return the smaller of {@code a} and {@code b}.
*/ */
@HotSpotIntrinsicCandidate
public static int min(int a, int b) { public static int min(int a, int b) {
return Math.min(a, b); return Math.min(a, b);
} }

View File

@ -42,6 +42,7 @@ import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; import java.util.regex.PatternSyntaxException;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The {@code String} class represents character strings. All * The {@code String} class represents character strings. All
@ -152,6 +153,7 @@ public final class String
* @param original * @param original
* A {@code String} * A {@code String}
*/ */
@HotSpotIntrinsicCandidate
public String(String original) { public String(String original) {
this.value = original.value; this.value = original.value;
this.hash = original.hash; this.hash = original.hash;
@ -978,6 +980,7 @@ public final class String
* @see #compareTo(String) * @see #compareTo(String)
* @see #equalsIgnoreCase(String) * @see #equalsIgnoreCase(String)
*/ */
@HotSpotIntrinsicCandidate
public boolean equals(Object anObject) { public boolean equals(Object anObject) {
if (this == anObject) { if (this == anObject) {
return true; return true;
@ -1154,6 +1157,7 @@ public final class String
* value greater than {@code 0} if this string is * value greater than {@code 0} if this string is
* lexicographically greater than the string argument. * lexicographically greater than the string argument.
*/ */
@HotSpotIntrinsicCandidate
public int compareTo(String anotherString) { public int compareTo(String anotherString) {
char[] v1 = value; char[] v1 = value;
char[] v2 = anotherString.value; char[] v2 = anotherString.value;
@ -1696,6 +1700,7 @@ public final class String
* @return the index of the first occurrence of the specified substring, * @return the index of the first occurrence of the specified substring,
* or {@code -1} if there is no such occurrence. * or {@code -1} if there is no such occurrence.
*/ */
@HotSpotIntrinsicCandidate
public int indexOf(String str) { public int indexOf(String str) {
return indexOf(str, 0); return indexOf(str, 0);
} }

View File

@ -26,6 +26,7 @@
package java.lang; package java.lang;
import java.util.Arrays; import java.util.Arrays;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* A thread-safe, mutable sequence of characters. * 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 * Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters. * initial capacity of 16 characters.
*/ */
@HotSpotIntrinsicCandidate
public StringBuffer() { public StringBuffer() {
super(16); super(16);
} }
@ -124,6 +126,7 @@ import java.util.Arrays;
* @exception NegativeArraySizeException if the {@code capacity} * @exception NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}. * argument is less than {@code 0}.
*/ */
@HotSpotIntrinsicCandidate
public StringBuffer(int capacity) { public StringBuffer(int capacity) {
super(capacity); super(capacity);
} }
@ -135,6 +138,7 @@ import java.util.Arrays;
* *
* @param str the initial contents of the buffer. * @param str the initial contents of the buffer.
*/ */
@HotSpotIntrinsicCandidate
public StringBuffer(String str) { public StringBuffer(String str) {
super(str.length() + 16); super(str.length() + 16);
append(str); append(str);
@ -271,6 +275,7 @@ import java.util.Arrays;
} }
@Override @Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(String str) { public synchronized StringBuffer append(String str) {
toStringCache = null; toStringCache = null;
super.append(str); super.append(str);
@ -382,6 +387,7 @@ import java.util.Arrays;
} }
@Override @Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(char c) { public synchronized StringBuffer append(char c) {
toStringCache = null; toStringCache = null;
super.append(c); super.append(c);
@ -389,6 +395,7 @@ import java.util.Arrays;
} }
@Override @Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(int i) { public synchronized StringBuffer append(int i) {
toStringCache = null; toStringCache = null;
super.append(i); super.append(i);
@ -670,6 +677,7 @@ import java.util.Arrays;
} }
@Override @Override
@HotSpotIntrinsicCandidate
public synchronized String toString() { public synchronized String toString() {
if (toStringCache == null) { if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count); toStringCache = Arrays.copyOfRange(value, 0, count);

View File

@ -25,6 +25,7 @@
package java.lang; package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* A mutable sequence of characters. This class provides an API compatible * 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 * Constructs a string builder with no characters in it and an
* initial capacity of 16 characters. * initial capacity of 16 characters.
*/ */
@HotSpotIntrinsicCandidate
public StringBuilder() { public StringBuilder() {
super(16); super(16);
} }
@ -97,6 +99,7 @@ public final class StringBuilder
* @throws NegativeArraySizeException if the {@code capacity} * @throws NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}. * argument is less than {@code 0}.
*/ */
@HotSpotIntrinsicCandidate
public StringBuilder(int capacity) { public StringBuilder(int capacity) {
super(capacity); super(capacity);
} }
@ -108,6 +111,7 @@ public final class StringBuilder
* *
* @param str the initial contents of the buffer. * @param str the initial contents of the buffer.
*/ */
@HotSpotIntrinsicCandidate
public StringBuilder(String str) { public StringBuilder(String str) {
super(str.length() + 16); super(str.length() + 16);
append(str); append(str);
@ -132,6 +136,7 @@ public final class StringBuilder
} }
@Override @Override
@HotSpotIntrinsicCandidate
public StringBuilder append(String str) { public StringBuilder append(String str) {
super.append(str); super.append(str);
return this; return this;
@ -198,12 +203,14 @@ public final class StringBuilder
} }
@Override @Override
@HotSpotIntrinsicCandidate
public StringBuilder append(char c) { public StringBuilder append(char c) {
super.append(c); super.append(c);
return this; return this;
} }
@Override @Override
@HotSpotIntrinsicCandidate
public StringBuilder append(int i) { public StringBuilder append(int i) {
super.append(i); super.append(i);
return this; return this;
@ -402,6 +409,7 @@ public final class StringBuilder
} }
@Override @Override
@HotSpotIntrinsicCandidate
public String toString() { public String toString() {
// Create a copy, don't share the array // Create a copy, don't share the array
return new String(value, 0, count); return new String(value, 0, count);

View File

@ -42,6 +42,7 @@ import sun.reflect.CallerSensitive;
import sun.reflect.Reflection; import sun.reflect.Reflection;
import sun.security.util.SecurityConstants; import sun.security.util.SecurityConstants;
import sun.reflect.annotation.AnnotationType; import sun.reflect.annotation.AnnotationType;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The <code>System</code> class contains several useful class fields * 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. * the current time and midnight, January 1, 1970 UTC.
* @see java.util.Date * @see java.util.Date
*/ */
@HotSpotIntrinsicCandidate
public static native long currentTimeMillis(); public static native long currentTimeMillis();
/** /**
@ -392,6 +394,7 @@ public final class System {
* high-resolution time source, in nanoseconds * high-resolution time source, in nanoseconds
* @since 1.5 * @since 1.5
*/ */
@HotSpotIntrinsicCandidate
public static native long nanoTime(); public static native long nanoTime();
/** /**
@ -486,6 +489,7 @@ public final class System {
* @exception NullPointerException if either <code>src</code> or * @exception NullPointerException if either <code>src</code> or
* <code>dest</code> is <code>null</code>. * <code>dest</code> is <code>null</code>.
*/ */
@HotSpotIntrinsicCandidate
public static native void arraycopy(Object src, int srcPos, public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos, Object dest, int destPos,
int length); int length);
@ -501,6 +505,7 @@ public final class System {
* @return the hashCode * @return the hashCode
* @since 1.1 * @since 1.1
*/ */
@HotSpotIntrinsicCandidate
public static native int identityHashCode(Object x); public static native int identityHashCode(Object x);
/** /**

View File

@ -40,7 +40,7 @@ import sun.nio.ch.Interruptible;
import sun.reflect.CallerSensitive; import sun.reflect.CallerSensitive;
import sun.reflect.Reflection; import sun.reflect.Reflection;
import sun.security.util.SecurityConstants; import sun.security.util.SecurityConstants;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* A <i>thread</i> is a thread of execution in a program. The Java * 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. * @return the currently executing thread.
*/ */
@HotSpotIntrinsicCandidate
public static native Thread currentThread(); 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 * is reset or not based on the value of ClearInterrupted that is
* passed. * passed.
*/ */
@HotSpotIntrinsicCandidate
private native boolean isInterrupted(boolean ClearInterrupted); private native boolean isInterrupted(boolean ClearInterrupted);
/** /**

View File

@ -27,6 +27,7 @@ package java.lang.invoke;
import java.util.*; import java.util.*;
import jdk.internal.HotSpotIntrinsicCandidate;
import static java.lang.invoke.MethodHandleStatics.*; 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 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 * @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; 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 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 * @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; 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 * @param args the signature-polymorphic parameter list, statically represented using varargs
* @return the signature-polymorphic result, statically represented using {@code Object} * @return the signature-polymorphic result, statically represented using {@code Object}
*/ */
@HotSpotIntrinsicCandidate
/*non-public*/ final native @PolymorphicSignature Object invokeBasic(Object... args) throws Throwable; /*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 * @param args the signature-polymorphic parameter list, statically represented using varargs
* @return the signature-polymorphic result, statically represented using {@code Object} * @return the signature-polymorphic result, statically represented using {@code Object}
*/ */
@HotSpotIntrinsicCandidate
/*non-public*/ static native @PolymorphicSignature Object linkToVirtual(Object... args) throws Throwable; /*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 * @param args the signature-polymorphic parameter list, statically represented using varargs
* @return the signature-polymorphic result, statically represented using {@code Object} * @return the signature-polymorphic result, statically represented using {@code Object}
*/ */
@HotSpotIntrinsicCandidate
/*non-public*/ static native @PolymorphicSignature Object linkToStatic(Object... args) throws Throwable; /*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 * @param args the signature-polymorphic parameter list, statically represented using varargs
* @return the signature-polymorphic result, statically represented using {@code Object} * @return the signature-polymorphic result, statically represented using {@code Object}
*/ */
@HotSpotIntrinsicCandidate
/*non-public*/ static native @PolymorphicSignature Object linkToSpecial(Object... args) throws Throwable; /*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 * @param args the signature-polymorphic parameter list, statically represented using varargs
* @return the signature-polymorphic result, statically represented using {@code Object} * @return the signature-polymorphic result, statically represented using {@code Object}
*/ */
@HotSpotIntrinsicCandidate
/*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args) throws Throwable; /*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args) throws Throwable;
/** /**

View File

@ -36,6 +36,7 @@ import sun.invoke.empty.Empty;
import sun.invoke.util.ValueConversions; import sun.invoke.util.ValueConversions;
import sun.invoke.util.VerifyType; import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper; import sun.invoke.util.Wrapper;
import jdk.internal.HotSpotIntrinsicCandidate;
import sun.reflect.CallerSensitive; import sun.reflect.CallerSensitive;
import sun.reflect.Reflection; import sun.reflect.Reflection;
import static java.lang.invoke.LambdaForm.*; 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. // Intrinsified by C2. Counters are used during parsing to calculate branch frequencies.
@LambdaForm.Hidden @LambdaForm.Hidden
@jdk.internal.HotSpotIntrinsicCandidate
static static
boolean profileBoolean(boolean result, int[] counters) { boolean profileBoolean(boolean result, int[] counters) {
// Profile is int[2] where [0] and [1] correspond to false and true occurrences respectively. // 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. // Intrinsified by C2. Returns true if obj is a compile-time constant.
@LambdaForm.Hidden @LambdaForm.Hidden
@jdk.internal.HotSpotIntrinsicCandidate
static static
boolean isCompileConstant(Object obj) { boolean isCompileConstant(Object obj) {
return false; return false;

View File

@ -29,6 +29,7 @@ import sun.misc.Cleaner;
import sun.misc.JavaLangRefAccess; import sun.misc.JavaLangRefAccess;
import sun.misc.ManagedLocalsThread; import sun.misc.ManagedLocalsThread;
import sun.misc.SharedSecrets; import sun.misc.SharedSecrets;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* Abstract base class for reference objects. This class defines the * 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 * @return The object to which this reference refers, or
* <code>null</code> if this reference object has been cleared * <code>null</code> if this reference object has been cleared
*/ */
@HotSpotIntrinsicCandidate
public T get() { public T get() {
return this.referent; return this.referent;
} }

View File

@ -25,6 +25,8 @@
package java.lang.reflect; package java.lang.reflect;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* The {@code Array} class provides static methods to dynamically create and * The {@code Array} class provides static methods to dynamically create and
* access Java arrays. * access Java arrays.
@ -119,6 +121,7 @@ class Array {
* @exception IllegalArgumentException if the object argument is not * @exception IllegalArgumentException if the object argument is not
* an array * an array
*/ */
@HotSpotIntrinsicCandidate
public static native int getLength(Object array) public static native int getLength(Object array)
throws IllegalArgumentException; throws IllegalArgumentException;
@ -477,6 +480,7 @@ class Array {
* Private * Private
*/ */
@HotSpotIntrinsicCandidate
private static native Object newArray(Class<?> componentType, int length) private static native Object newArray(Class<?> componentType, int length)
throws NegativeArraySizeException; throws NegativeArraySizeException;

View File

@ -25,6 +25,7 @@
package java.lang.reflect; package java.lang.reflect;
import jdk.internal.HotSpotIntrinsicCandidate;
import sun.reflect.CallerSensitive; import sun.reflect.CallerSensitive;
import sun.reflect.MethodAccessor; import sun.reflect.MethodAccessor;
import sun.reflect.Reflection; import sun.reflect.Reflection;
@ -485,6 +486,7 @@ public final class Method extends Executable {
* provoked by this method fails. * provoked by this method fails.
*/ */
@CallerSensitive @CallerSensitive
@HotSpotIntrinsicCandidate
public Object invoke(Object obj, Object... args) public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException, throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException InvocationTargetException

View File

@ -34,10 +34,13 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.ObjectStreamField; import java.io.ObjectStreamField;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import sun.misc.DoubleConsts; import sun.misc.DoubleConsts;
import sun.misc.FloatConsts; import sun.misc.FloatConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* Immutable arbitrary-precision integers. All operations behave as if * 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; 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 // 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 * 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. * 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 xstart = xlen - 1;
int ystart = ylen - 1; int ystart = ylen - 1;
@ -1669,6 +1688,18 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
return z; 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 * Multiplies two BigIntegers using the Karatsuba multiplication
* algorithm. This is a recursive divide-and-conquer algorithm which is * 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. * Java Runtime may use intrinsic for this method.
*/ */
@HotSpotIntrinsicCandidate
private static final int[] implSquareToLen(int[] x, int len, int[] z, int zlen) { 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. * 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); 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, static int[] bnExpModThreshTable = {7, 25, 81, 241, 673, 1793,
Integer.MAX_VALUE}; // Sentinel Integer.MAX_VALUE}; // Sentinel
@ -2679,6 +2782,17 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
int[] mod = z.mag; int[] mod = z.mag;
int modLen = mod.length; 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 // Select an appropriate window size
int wbits = 0; int wbits = 0;
int ebits = bitLength(exp, exp.length); 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++) for (int i=0; i < tblmask; i++)
table[i] = new int[modLen]; table[i] = new int[modLen];
// Compute the modular inverse // Compute the modular inverse of the least significant 64-bit
int inv = -MutableBigInteger.inverseMod32(mod[modLen-1]); // 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 // Convert base to Montgomery form
int[] a = leftShift(base, base.length, modLen << 5); int[] a = leftShift(base, base.length, modLen << 5);
@ -2706,6 +2822,8 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
MutableBigInteger q = new MutableBigInteger(), MutableBigInteger q = new MutableBigInteger(),
a2 = new MutableBigInteger(a), a2 = new MutableBigInteger(a),
b2 = new MutableBigInteger(mod); b2 = new MutableBigInteger(mod);
b2.normalize(); // MutableBigInteger.divide() assumes that its
// divisor is in normal form.
MutableBigInteger r= a2.divide(b2, q); MutableBigInteger r= a2.divide(b2, q);
table[0] = r.toIntArray(); table[0] = r.toIntArray();
@ -2714,22 +2832,19 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
if (table[0].length < modLen) { if (table[0].length < modLen) {
int offset = modLen - table[0].length; int offset = modLen - table[0].length;
int[] t2 = new int[modLen]; int[] t2 = new int[modLen];
for (int i=0; i < table[0].length; i++) System.arraycopy(table[0], 0, t2, offset, table[0].length);
t2[i+offset] = table[0][i];
table[0] = t2; table[0] = t2;
} }
// Set b to the square of the base // Set b to the square of the base
int[] b = squareToLen(table[0], modLen, null); int[] b = montgomerySquare(table[0], mod, modLen, inv, null);
b = montReduce(b, mod, modLen, inv);
// Set t to high half of b // Set t to high half of b
int[] t = Arrays.copyOf(b, modLen); int[] t = Arrays.copyOf(b, modLen);
// Fill in the table with odd powers of the base // Fill in the table with odd powers of the base
for (int i=1; i < tblmask; i++) { for (int i=1; i < tblmask; i++) {
int[] prod = multiplyToLen(t, modLen, table[i-1], modLen, null); table[i] = montgomeryMultiply(t, table[i-1], mod, modLen, inv, null);
table[i] = montReduce(prod, mod, modLen, inv);
} }
// Pre load the window that slides over the exponent // Pre load the window that slides over the exponent
@ -2800,8 +2915,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
isone = false; isone = false;
} else { } else {
t = b; t = b;
a = multiplyToLen(t, modLen, mult, modLen, a); a = montgomeryMultiply(t, mult, mod, modLen, inv, a);
a = montReduce(a, mod, modLen, inv);
t = a; a = b; b = t; t = a; a = b; b = t;
} }
} }
@ -2813,8 +2927,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
// Square the input // Square the input
if (!isone) { if (!isone) {
t = b; t = b;
a = squareToLen(t, modLen, a); a = montgomerySquare(t, mod, modLen, inv, a);
a = montReduce(a, mod, modLen, inv);
t = a; a = b; b = t; t = a; a = b; b = t;
} }
} }
@ -2823,7 +2936,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
int[] t2 = new int[2*modLen]; int[] t2 = new int[2*modLen];
System.arraycopy(b, 0, t2, modLen, 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); 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. * Java Runtime may use intrinsic for this method.
*/ */
@HotSpotIntrinsicCandidate
private static int implMulAdd(int[] out, int[] in, int offset, int len, int k) { private static int implMulAdd(int[] out, int[] in, int offset, int len, int k) {
long kLong = k & LONG_MASK; long kLong = k & LONG_MASK;
long carry = 0; long carry = 0;

View File

@ -2064,6 +2064,21 @@ class MutableBigInteger {
return t; 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. * Calculate the multiplicative inverse of 2^k mod mod, where mod is odd.
*/ */

View File

@ -26,6 +26,7 @@
package java.nio; package java.nio;
import java.util.Spliterator; import java.util.Spliterator;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* A container for data of a specific primitive type. * 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 * IndexOutOfBoundsException} if it is not smaller than the limit
* or is smaller than zero. * or is smaller than zero.
*/ */
@HotSpotIntrinsicCandidate
final int checkIndex(int i) { // package-private final int checkIndex(int i) { // package-private
if ((i < 0) || (i >= limit)) if ((i < 0) || (i >= limit))
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();

View File

@ -42,6 +42,7 @@ import java.util.stream.IntStream;
import java.util.stream.LongStream; import java.util.stream.LongStream;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* This class contains various methods for manipulating arrays (such as * 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 * @param a2 the other array to be tested for equality
* @return <tt>true</tt> if the two arrays are equal * @return <tt>true</tt> if the two arrays are equal
*/ */
@HotSpotIntrinsicCandidate
public static boolean equals(char[] a, char[] a2) { public static boolean equals(char[] a, char[] a2) {
if (a==a2) if (a==a2)
return true; return true;
@ -3205,6 +3207,7 @@ public class Arrays {
* an array of class <tt>newType</tt> * an array of class <tt>newType</tt>
* @since 1.6 * @since 1.6
*/ */
@HotSpotIntrinsicCandidate
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class) T[] copy = ((Object)newType == (Object)Object[].class)
@ -3474,6 +3477,7 @@ public class Arrays {
* an array of class <tt>newType</tt>. * an array of class <tt>newType</tt>.
* @since 1.6 * @since 1.6
*/ */
@HotSpotIntrinsicCandidate
public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) { public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
int newLength = to - from; int newLength = to - from;
if (newLength < 0) if (newLength < 0)

View File

@ -31,6 +31,7 @@ import java.util.function.Consumer;
import java.util.function.DoubleConsumer; import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer; import java.util.function.IntConsumer;
import java.util.function.LongConsumer; import java.util.function.LongConsumer;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* Utility methods for operating on and creating streams. * Utility methods for operating on and creating streams.
@ -98,6 +99,7 @@ final class Streams {
} }
@Override @Override
@HotSpotIntrinsicCandidate
public void forEachRemaining(IntConsumer consumer) { public void forEachRemaining(IntConsumer consumer) {
Objects.requireNonNull(consumer); Objects.requireNonNull(consumer);

View File

@ -26,7 +26,10 @@
package java.util.zip; package java.util.zip;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Objects;
import sun.nio.ch.DirectBuffer; import sun.nio.ch.DirectBuffer;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* A class that can be used to compute the CRC-32 of a data stream. * 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; return (long)crc & 0xffffffffL;
} }
@HotSpotIntrinsicCandidate
private native static int update(int crc, int b); 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, private static int updateBytes(int crc, byte[] b, int off, int len) {
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();
}
}
} }

View File

@ -26,6 +26,8 @@ package java.util.zip;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import jdk.internal.HotSpotIntrinsicCandidate;
import sun.misc.Unsafe; import sun.misc.Unsafe;
import sun.nio.ch.DirectBuffer; 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. * Updates the CRC-32C checksum with the specified array of bytes.
*/ */
@HotSpotIntrinsicCandidate
private static int updateBytes(int crc, byte[] b, int off, int end) { 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 // 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. * Updates the CRC-32C checksum reading from the specified address.
*/ */
@HotSpotIntrinsicCandidate
private static int updateDirectByteBuffer(int crc, long address, private static int updateDirectByteBuffer(int crc, long address,
int off, int end) { int off, int end) {

View File

@ -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 {
}

View File

@ -24,42 +24,95 @@
*/ */
package jdk.internal.jimage; package jdk.internal.jimage;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.util.stream.Stream;
import java.nio.file.Path;
import java.util.function.Consumer;
/** /**
* An Archive of all content, classes, resources, configuration files, and * An Archive of all content, classes, resources, configuration files, and
* other, for a module. * other, for a module.
*/ */
public interface Archive { 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. * The module name.
*/ */
String moduleName(); 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 { void close() throws IOException;
String getName();
InputStream getInputStream();
boolean isDirectory();
}
/**
* A Consumer suitable for writing Entries from this Archive.
*/
Consumer<Entry> defaultImageWriter(Path path, OutputStream out);
} }

View File

@ -24,63 +24,88 @@
*/ */
package jdk.internal.jimage; package jdk.internal.jimage;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.File;
import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.MappedByteBuffer; import java.util.Comparator;
import java.nio.channels.FileChannel; import java.util.stream.IntStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BasicImageReader { public class BasicImageReader {
private final String imagePath; private final String imagePath;
private final PReader preader; private final ImageSubstrate substrate;
private final ByteOrder byteOrder; private final ByteOrder byteOrder;
private final ImageHeader header; private final ImageStringsReader strings;
private final int indexSize;
private final IntBuffer redirectBuffer;
private final IntBuffer offsetsBuffer;
private final ByteBuffer locationsBuffer;
private final ByteBuffer stringsBuffer;
private final ImageStrings strings;
protected BasicImageReader(String imagePath, ByteOrder byteOrder) throws IOException { protected BasicImageReader(String imagePath, ByteOrder byteOrder)
throws IOException {
this.imagePath = imagePath; this.imagePath = imagePath;
this.preader = PReader.open(imagePath); this.substrate = openImageSubstrate(imagePath, byteOrder);
this.byteOrder = byteOrder; this.byteOrder = byteOrder;
this.header = ImageHeader.readFrom(byteOrder, getIntBuffer(0, ImageHeader.getHeaderSize())); this.strings = new ImageStringsReader(this);
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));
} }
protected BasicImageReader(String imagePath) throws IOException { protected BasicImageReader(String imagePath) throws IOException {
this(imagePath, ByteOrder.nativeOrder()); 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 { public static BasicImageReader open(String imagePath) throws IOException {
return new BasicImageReader(imagePath, ByteOrder.nativeOrder()); return new BasicImageReader(imagePath, ByteOrder.nativeOrder());
} }
public static void releaseByteBuffer(ByteBuffer buffer) {
ImageBufferCache.releaseBuffer(buffer);
}
public ByteOrder getByteOrder() {
return byteOrder;
}
public String imagePath() { public String imagePath() {
return 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() { public boolean isOpen() {
return preader.isOpen(); return true;
} }
public void close() throws IOException { public void close() throws IOException {
preader.close(); substrate.close();
} }
public ImageHeader getHeader() { public ImageHeader getHeader() throws IOException {
return header; return ImageHeader.readFrom(
getIndexIntBuffer(0, ImageHeader.getHeaderSize()));
}
public ImageStringsReader getStrings() {
return strings;
} }
public ImageLocation findLocation(String name) { public ImageLocation findLocation(String name) {
@ -92,148 +117,147 @@ public class BasicImageReader {
} }
public synchronized ImageLocation findLocation(UTF8String name) { public synchronized ImageLocation findLocation(UTF8String name) {
int count = header.getLocationCount(); return substrate.findLocation(name, strings);
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;
} }
public String[] getEntryNames() { public String[] getEntryNames() {
return getEntryNames(true); return IntStream.of(substrate.attributeOffsets())
} .filter(o -> o != 0)
.mapToObj(o -> ImageLocation.readFrom(this, o).getFullNameString())
public String[] getEntryNames(boolean sorted) { .sorted()
int count = header.getLocationCount(); .toArray(String[]::new);
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;
} }
protected ImageLocation[] getAllLocations(boolean sorted) { protected ImageLocation[] getAllLocations(boolean sorted) {
int count = header.getLocationCount(); return IntStream.of(substrate.attributeOffsets())
List<ImageLocation> list = new ArrayList<>(); .filter(o -> o != 0)
.mapToObj(o -> ImageLocation.readFrom(this, o))
for (int i = 0; i < count; i++) { .sorted(Comparator.comparing(ImageLocation::getFullNameString))
int offset = getOffset(i); .toArray(ImageLocation[]::new);
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;
} }
private IntBuffer getIntBuffer(long offset, long size) throws IOException { private IntBuffer getIndexIntBuffer(long offset, long size)
MappedByteBuffer buffer = preader.channel().map(FileChannel.MapMode.READ_ONLY, offset, size); throws IOException {
ByteBuffer buffer = substrate.getIndexBuffer(offset, size);
buffer.order(byteOrder); buffer.order(byteOrder);
return buffer.asIntBuffer(); return buffer.asIntBuffer();
} }
private ByteBuffer getByteBuffer(long offset, long size) throws IOException { ImageLocation getLocation(int offset) {
MappedByteBuffer buffer = preader.channel().map(FileChannel.MapMode.READ_ONLY, offset, size); return ImageLocation.readFrom(this, offset);
// order is not copied into the readonly copy.
ByteBuffer readOnly = buffer.asReadOnlyBuffer();
readOnly.order(byteOrder);
return readOnly;
} }
private int getRedirect(int index) { public long[] getAttributes(int offset) {
return redirectBuffer.get(index); return substrate.getAttributes(offset);
}
private int getOffset(int index) {
return offsetsBuffer.get(index);
}
private ImageLocation getLocation(int offset) {
return ImageLocation.readFrom(locationsBuffer, offset, strings);
} }
public String getString(int 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 compressedSize = loc.getCompressedSize();
long uncompressedSize = loc.getUncompressedSize();
assert compressedSize < Integer.MAX_VALUE; assert compressedSize < Integer.MAX_VALUE;
assert uncompressedSize < Integer.MAX_VALUE;
if (compressedSize == 0) { if (substrate.supportsDataBuffer() && compressedSize == 0) {
return preader.read((int)loc.getUncompressedSize(), return getBufferBytes(offset, uncompressedSize);
indexSize + loc.getContentOffset());
} else {
byte[] buf = preader.read((int)compressedSize,
indexSize + loc.getContentOffset());
return ImageFile.Compressor.decompress(buf);
} }
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); ImageLocation location = findLocation(name);
return location != null ? getResource(location) : null; return location != null ? getResource(location) : null;
} }
public List<String> getNames(String name) throws IOException { public ByteBuffer getResourceBuffer(ImageLocation loc) {
return getNames(getResource(name)); 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) { if (substrate.supportsDataBuffer() && compressedSize == 0) {
IntBuffer buffer = ByteBuffer.wrap(bytes).asIntBuffer(); return substrate.getDataBuffer(offset, uncompressedSize);
List<String> names = new ArrayList<>();
while (buffer.hasRemaining()) {
int offset = buffer.get();
names.add(getString(offset));
} }
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;
} }
} }

View File

@ -25,67 +25,30 @@
package jdk.internal.jimage; package jdk.internal.jimage;
import java.io.PrintStream;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
public final class BasicImageWriter { 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 final static int RETRY_LIMIT = 1000;
private ByteOrder byteOrder; private ByteOrder byteOrder;
private ImageStrings strings; private ImageStringsWriter strings;
private int count; private int length;
private int[] redirect; private int[] redirect;
private ImageLocation[] locations; private ImageLocationWriter[] locations;
private List<ImageLocation> input; private List<ImageLocationWriter> input;
private ImageStream headerStream; private ImageStream headerStream;
private ImageStream redirectStream; private ImageStream redirectStream;
private ImageStream locationOffsetStream; private ImageStream locationOffsetStream;
private ImageStream locationStream; private ImageStream locationStream;
private ImageStream allIndexStream; 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() { public BasicImageWriter() {
this(ByteOrder.nativeOrder()); this(ByteOrder.nativeOrder());
} }
@ -93,7 +56,7 @@ public final class BasicImageWriter {
public BasicImageWriter(ByteOrder byteOrder) { public BasicImageWriter(ByteOrder byteOrder) {
this.byteOrder = byteOrder; this.byteOrder = byteOrder;
this.input = new ArrayList<>(); this.input = new ArrayList<>();
this.strings = new ImageStrings(); this.strings = new ImageStringsWriter();
this.headerStream = new ImageStream(byteOrder); this.headerStream = new ImageStream(byteOrder);
this.redirectStream = new ImageStream(byteOrder); this.redirectStream = new ImageStream(byteOrder);
this.locationOffsetStream = new ImageStream(byteOrder); this.locationOffsetStream = new ImageStream(byteOrder);
@ -101,6 +64,10 @@ public final class BasicImageWriter {
this.allIndexStream = new ImageStream(byteOrder); this.allIndexStream = new ImageStream(byteOrder);
} }
public ByteOrder getByteOrder() {
return byteOrder;
}
public int addString(String string) { public int addString(String string) {
return addString(new UTF8String(string)); return addString(new UTF8String(string));
} }
@ -109,104 +76,48 @@ public final class BasicImageWriter {
return strings.add(string); return strings.add(string);
} }
public void addLocation(String fullname, long contentOffset, long compressedSize, long uncompressedSize) { public String getString(int offset) {
ImageLocation location = ImageLocation.newLocation(new UTF8String(fullname), strings, contentOffset, compressedSize, uncompressedSize); 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); input.add(location);
count++; length++;
}
ImageLocationWriter[] getLocations() {
return locations;
}
int getLocationsCount() {
return input.size();
} }
private void generatePerfectHash() { private void generatePerfectHash() {
redo: PerfectHashBuilder<ImageLocationWriter> builder =
while(true) { new PerfectHashBuilder<>(
redirect = new int[count]; new PerfectHashBuilder.Entry<ImageLocationWriter>().getClass(),
locations = new ImageLocation[count]; new PerfectHashBuilder.Bucket<ImageLocationWriter>().getClass());
ImageBucket[] sorted = createBuckets(); input.forEach((location) -> {
builder.put(location.getFullName(), location);
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);
}); });
ImageBucket[] sorted = Arrays.asList(buckets).stream() builder.generate();
.filter((bucket) -> (bucket != null))
.sorted()
.toArray(ImageBucket[]::new);
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) { for (int i = 0; i < length; i++) {
List<Integer> undo = new ArrayList<>(); locations[i] = order[i].getValue();
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;
} }
return true;
} }
private void prepareStringBytes() { private void prepareStringBytes() {
@ -214,17 +125,17 @@ public final class BasicImageWriter {
} }
private void prepareRedirectBytes() { private void prepareRedirectBytes() {
for (int i = 0; i < count; i++) { for (int i = 0; i < length; i++) {
redirectStream.putInt(redirect[i]); redirectStream.putInt(redirect[i]);
} }
} }
private void prepareLocationBytes() { private void prepareLocationBytes() {
// Reserve location offset zero for empty locations // 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++) { for (int i = 0; i < length; i++) {
ImageLocation location = locations[i]; ImageLocationWriter location = locations[i];
if (location != null) { if (location != null) {
location.writeTo(locationStream); location.writeTo(locationStream);
@ -235,14 +146,16 @@ public final class BasicImageWriter {
} }
private void prepareOffsetBytes() { private void prepareOffsetBytes() {
for (int i = 0; i < count; i++) { for (int i = 0; i < length; i++) {
ImageLocation location = locations[i]; ImageLocationWriter location = locations[i];
locationOffsetStream.putInt(location != null ? location.getLocationOffset() : 0); int offset = location != null ? location.getLocationOffset() : 0;
locationOffsetStream.putInt(offset);
} }
} }
private void prepareHeaderBytes() { 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); header.writeTo(headerStream);
} }
@ -268,33 +181,15 @@ public final class BasicImageWriter {
return allIndexStream.toArray(); return allIndexStream.toArray();
} }
ImageLocation find(UTF8String key) { ImageLocationWriter find(UTF8String key) {
int index = key.hashCode() % count; int index = redirect[key.hashCode() % length];
index = redirect[index];
if (index < 0) { if (index < 0) {
index = -index - 1; index = -index - 1;
ImageLocation location = locations[index];
return location;
} else { } else {
index = key.hashCode(index) % count; index = key.hashCode(index) % length;
ImageLocation location = locations[index];
return location;
} }
}
public void statistics() { return locations[index];
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());
} }
} }

View File

@ -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";
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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 "";
}
}
}

View File

@ -25,67 +25,76 @@
package jdk.internal.jimage; package jdk.internal.jimage;
import java.nio.ByteOrder; import java.nio.ByteBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
public final class ImageHeader { public final class ImageHeader {
public static final int MAGIC = 0xCAFEDADA; public static final int MAGIC = 0xCAFEDADA;
public static final int BADMAGIC = 0xDADAFECA; public static final int BADMAGIC = 0xDADAFECA;
public static final short MAJOR_VERSION = 0; public static final int MAJOR_VERSION = 1;
public static final short MINOR_VERSION = 1; public static final int MINOR_VERSION = 0;
private final int magic; private final int magic;
private final short majorVersion; private final int majorVersion;
private final short minorVersion; private final int minorVersion;
private final int locationCount; private final int flags;
private final int resourceCount;
private final int tableLength;
private final int locationsSize; private final int locationsSize;
private final int stringsSize; private final int stringsSize;
ImageHeader(int locationCount, int locationsSize, int stringsSize) { public ImageHeader(int resourceCount, int tableCount,
this(MAGIC, MAJOR_VERSION, MINOR_VERSION, locationCount, locationsSize, stringsSize); int locationsSize, int stringsSize) {
this(MAGIC, MAJOR_VERSION, MINOR_VERSION, 0, resourceCount,
tableCount, locationsSize, stringsSize);
} }
ImageHeader(int magic, short majorVersion, short minorVersion, int locationCount, public ImageHeader(int magic, int majorVersion, int minorVersion,
int locationsSize, int stringsSize) int flags, int resourceCount,
int tableLength, int locationsSize, int stringsSize)
{ {
this.magic = magic; this.magic = magic;
this.majorVersion = majorVersion; this.majorVersion = majorVersion;
this.minorVersion = minorVersion; this.minorVersion = minorVersion;
this.locationCount = locationCount; this.flags = flags;
this.resourceCount = resourceCount;
this.tableLength = tableLength;
this.locationsSize = locationsSize; this.locationsSize = locationsSize;
this.stringsSize = stringsSize; this.stringsSize = stringsSize;
} }
static int getHeaderSize() { public static int getHeaderSize() {
return 4 + return 7 * 4;
2 + 2 +
4 +
4 +
4;
} }
static ImageHeader readFrom(ByteOrder byteOrder, IntBuffer buffer) { static ImageHeader readFrom(IntBuffer buffer) {
int magic = buffer.get(0); int magic = buffer.get(0);
int version = buffer.get(1); int version = buffer.get(1);
short majorVersion = (short)(byteOrder == ByteOrder.BIG_ENDIAN ? int majorVersion = version >>> 16;
version >>> 16 : (version & 0xFFFF)); int minorVersion = version & 0xFFFF;
short minorVersion = (short)(byteOrder == ByteOrder.BIG_ENDIAN ? int flags = buffer.get(2);
(version & 0xFFFF) : version >>> 16); int resourceCount = buffer.get(3);
int locationCount = buffer.get(2); int tableLength = buffer.get(4);
int locationsSize = buffer.get(3); int locationsSize = buffer.get(5);
int stringsSize = buffer.get(4); int stringsSize = buffer.get(6);
return new ImageHeader(magic, majorVersion, minorVersion, locationCount, return new ImageHeader(magic, majorVersion, minorVersion, flags,
locationsSize, stringsSize); resourceCount, tableLength, locationsSize, stringsSize);
} }
void writeTo(ImageStream stream) { void writeTo(ImageStream stream) {
stream.putInt(magic); stream.ensure(getHeaderSize());
stream.putShort(majorVersion); writeTo(stream.getBuffer());
stream.putShort(minorVersion); }
stream.putInt(locationCount);
stream.putInt(locationsSize); public void writeTo(ByteBuffer buffer) {
stream.putInt(stringsSize); 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() { public int getMagic() {
@ -100,16 +109,24 @@ public final class ImageHeader {
return minorVersion; return minorVersion;
} }
public int getLocationCount() { public int getFlags() {
return locationCount; return flags;
}
public int getResourceCount() {
return resourceCount;
}
public int getTableLength() {
return tableLength;
} }
public int getRedirectSize() { public int getRedirectSize() {
return locationCount* 4; return tableLength * 4;
} }
public int getOffsetsSize() { public int getOffsetsSize() {
return locationCount* 4; return tableLength * 4;
} }
public int getLocationsSize() { public int getLocationsSize() {

View File

@ -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;
}
}

View File

@ -25,369 +25,15 @@
package jdk.internal.jimage; package jdk.internal.jimage;
import java.nio.ByteBuffer; public final class ImageLocation extends ImageLocationBase {
ImageLocation(long[] attributes, ImageStringsReader strings) {
public final class ImageLocation { super(attributes, strings);
final static int ATTRIBUTE_END = 0;
final static int ATTRIBUTE_BASE = 1;
final static int ATTRIBUTE_PARENT = 2;
final static int ATTRIBUTE_EXTENSION = 3;
final static int ATTRIBUTE_OFFSET = 4;
final static int ATTRIBUTE_COMPRESSED = 5;
final static int ATTRIBUTE_UNCOMPRESSED = 6;
final static int ATTRIBUTE_COUNT = 7;
private int locationOffset;
private long[] attributes;
private byte[] bytes;
private final ImageStrings strings;
private ImageLocation(ImageStrings strings) {
this.strings = strings;
} }
void writeTo(ImageStream stream) { static ImageLocation readFrom(BasicImageReader reader, int offset) {
compress(); long[] attributes = reader.getAttributes(offset);
locationOffset = stream.getPosition(); ImageStringsReader strings = reader.getStrings();
stream.put(bytes, 0, bytes.length);
}
static ImageLocation readFrom(ByteBuffer locationsBuffer, int offset, ImageStrings strings) { return new ImageLocation(attributes, 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();
} }
} }

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -26,12 +26,10 @@ package jdk.internal.jimage;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.FileSystem;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime; import java.nio.file.attribute.FileTime;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -42,13 +40,11 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import static jdk.internal.jimage.UTF8String.*;
public class ImageReader extends BasicImageReader { public class ImageReader extends BasicImageReader {
// well-known strings needed for image file system. // well-known strings needed for image file system.
static final UTF8String ROOT = new UTF8String("/"); static final UTF8String ROOT_STRING = UTF8String.SLASH_STRING;
static final UTF8String META_INF = new UTF8String("/META-INF");
static final UTF8String PACKAGES_OFFSETS = new UTF8String("packages.offsets");
// attributes of the .jimage file. jimage file does not contain // attributes of the .jimage file. jimage file does not contain
// attributes for the individual resources (yet). We use attributes // attributes for the individual resources (yet). We use attributes
@ -56,15 +52,18 @@ public class ImageReader extends BasicImageReader {
// Iniitalized lazily, see {@link #imageFileAttributes()}. // Iniitalized lazily, see {@link #imageFileAttributes()}.
private BasicFileAttributes imageFileAttributes; private BasicFileAttributes imageFileAttributes;
private final Map<String, String> packageMap; private final ImageModuleData moduleData;
// directory management implementation // directory management implementation
private final Map<UTF8String, Node> nodes; private final Map<UTF8String, Node> nodes;
private volatile Directory rootDir; private volatile Directory rootDir;
private Directory packagesDir;
private Directory modulesDir;
ImageReader(String imagePath, ByteOrder byteOrder) throws IOException { ImageReader(String imagePath, ByteOrder byteOrder) throws IOException {
super(imagePath, byteOrder); super(imagePath, byteOrder);
this.packageMap = PackageModuleMap.readFrom(this); this.moduleData = new ImageModuleData(this);
this.nodes = Collections.synchronizedMap(new HashMap<>()); this.nodes = Collections.synchronizedMap(new HashMap<>());
} }
@ -89,11 +88,42 @@ public class ImageReader extends BasicImageReader {
clearNodes(); 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. * Return the module name that contains the given package name.
*/ */
public String getModule(String pkg) { public String getModule(String packageName) {
return packageMap.get(pkg); return moduleData.packageToModule(packageName);
} }
// jimage file does not store directory structure. We build nodes // 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 // Node can be a directory or a resource
public static abstract class Node { public static abstract class Node {
private static final int ROOT_DIR = 0b0000_0000_0000_0001; private static final int ROOT_DIR = 0b0000_0000_0000_0001;
private static final int MODULE_DIR = 0b0000_0000_0000_0010; private static final int PACKAGES_DIR = 0b0000_0000_0000_0010;
private static final int METAINF_DIR = 0b0000_0000_0000_0100; private static final int MODULES_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 int flags; private int flags;
private final UTF8String name; private final UTF8String name;
private final BasicFileAttributes fileAttrs; private final BasicFileAttributes fileAttrs;
private boolean completed;
Node(UTF8String name, BasicFileAttributes fileAttrs) { Node(UTF8String name, BasicFileAttributes fileAttrs) {
assert name != null; assert name != null;
@ -117,6 +146,19 @@ public class ImageReader extends BasicImageReader {
this.fileAttrs = fileAttrs; 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() { public final void setIsRootDir() {
flags |= ROOT_DIR; flags |= ROOT_DIR;
} }
@ -125,40 +167,20 @@ public class ImageReader extends BasicImageReader {
return (flags & ROOT_DIR) != 0; return (flags & ROOT_DIR) != 0;
} }
public final void setIsModuleDir() { public final void setIsPackagesDir() {
flags |= MODULE_DIR; flags |= PACKAGES_DIR;
} }
public final boolean isModuleDir() { public final boolean isPackagesDir() {
return (flags & MODULE_DIR) != 0; return (flags & PACKAGES_DIR) != 0;
} }
public final void setIsMetaInfDir() { public final void setIsModulesDir() {
flags |= METAINF_DIR; flags |= MODULES_DIR;
} }
public final boolean isMetaInfDir() { public final boolean isModulesDir() {
return (flags & METAINF_DIR) != 0; return (flags & MODULES_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 UTF8String getName() { public final UTF8String getName() {
@ -169,6 +191,20 @@ public class ImageReader extends BasicImageReader {
return fileAttrs; 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() { public boolean isDirectory() {
return false; return false;
} }
@ -242,16 +278,20 @@ public class ImageReader extends BasicImageReader {
} }
// directory node - directory has full path name without '/' at end. // 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; private final List<Node> children;
@SuppressWarnings("LeakingThisInConstructor") private Directory(Directory parent, UTF8String name, BasicFileAttributes fileAttrs) {
Directory(Directory parent, UTF8String name, BasicFileAttributes fileAttrs) {
super(name, fileAttrs); super(name, fileAttrs);
children = new ArrayList<>(); children = new ArrayList<>();
}
static Directory create(Directory parent, UTF8String name, BasicFileAttributes fileAttrs) {
Directory dir = new Directory(parent, name, fileAttrs);
if (parent != null) { if (parent != null) {
parent.addChild(this); parent.addChild(dir);
} }
return dir;
} }
@Override @Override
@ -259,6 +299,7 @@ public class ImageReader extends BasicImageReader {
return true; return true;
} }
@Override
public List<Node> getChildren() { public List<Node> getChildren() {
return Collections.unmodifiableList(children); 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. // "resource" is .class or any other resource (compressed/uncompressed) in a jimage.
// full path of the resource is the "name" of the resource. // 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; private final ImageLocation loc;
@SuppressWarnings("LeakingThisInConstructor") private Resource(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) {
Resource(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) { this(parent, loc.getFullName(true), loc, fileAttrs);
this(parent, ROOT.concat(loc.getFullname()), loc, fileAttrs);
} }
@SuppressWarnings("LeakingThisInConstructor") private Resource(Directory parent, UTF8String name, ImageLocation loc, BasicFileAttributes fileAttrs) {
Resource(Directory parent, UTF8String name, ImageLocation loc, BasicFileAttributes fileAttrs) {
super(name, fileAttrs); super(name, fileAttrs);
this.loc = loc; 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 @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 // directory management interface
public Directory getRootDirectory() { public Directory getRootDirectory() {
return buildRootDirectory(); return buildRootDirectory();
@ -340,9 +426,154 @@ public class ImageReader extends BasicImageReader {
return findNode(new UTF8String(name)); 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) { public synchronized Node findNode(UTF8String name) {
buildRootDirectory(); 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() { private synchronized void clearNodes() {
@ -375,65 +606,61 @@ public class ImageReader extends BasicImageReader {
// FIXME no time information per resource in jimage file (yet?) // FIXME no time information per resource in jimage file (yet?)
// we use file attributes of jimage itself. // we use file attributes of jimage itself.
// root directory // root directory
rootDir = new Directory(null, ROOT, imageFileAttributes()); rootDir = newDirectory(null, ROOT_STRING);
rootDir.setIsRootDir(); rootDir.setIsRootDir();
nodes.put(rootDir.getName(), rootDir);
ImageLocation[] locs = getAllLocations(true); // /packages dir
for (ImageLocation loc : locs) { packagesDir = newDirectory(rootDir, PACKAGES_STRING);
UTF8String parent = loc.getParent(); packagesDir.setIsPackagesDir();
// 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);
}
Node metaInf = nodes.get(META_INF); // /modules dir
if (metaInf instanceof Directory) { modulesDir = newDirectory(rootDir, MODULES_STRING);
metaInf.setIsMetaInfDir(); modulesDir.setIsModulesDir();
((Directory)metaInf).walk(Node::setIsHidden);
}
fillPackageModuleInfo();
rootDir.setCompleted(true);
return rootDir; return rootDir;
} }
private Directory newDirectory(Directory parent, UTF8String name) { 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); nodes.put(dir.getName(), dir);
return dir; return dir;
} }
private Directory makeDirectories(UTF8String parent) { private Resource newResource(Directory parent, ImageLocation loc) {
assert !parent.isEmpty() : "non empty parent expected"; Resource res = Resource.create(parent, loc, imageFileAttributes());
nodes.put(res.getName(), res);
return res;
}
int idx = parent.indexOf('/'); private LinkNode newLinkNode(Directory dir, UTF8String name, Node link) {
assert idx != -1 : "invalid parent string"; LinkNode linkNode = LinkNode.create(dir, name, link);
UTF8String name = ROOT.concat(parent.substring(0, idx)); nodes.put(linkNode.getName(), linkNode);
Directory top = (Directory) nodes.get(name); return linkNode;
if (top == null) { }
top = newDirectory(rootDir, name);
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) { splits.add(parent);
name = ROOT.concat(parent.substring(0, idx));
Directory nextDir = (Directory) nodes.get(name); 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) { if (nextDir == null) {
nextDir = newDirectory(last, name); nextDir = newDirectory(last, dir);
} }
last = nextDir; last = nextDir;
} }
@ -441,54 +668,6 @@ public class ImageReader extends BasicImageReader {
return last; 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 { public byte[] getResource(Node node) throws IOException {
if (node.isResource()) { if (node.isResource()) {
return super.getResource(node.getLocation()); return super.getResource(node.getLocation());

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -72,7 +72,7 @@ class ImageStream {
return this; return this;
} }
private void ensure(int needs) { void ensure(int needs) {
assert 0 <= needs : "Negative needs"; assert 0 <= needs : "Negative needs";
if (needs > buffer.remaining()) { if (needs > buffer.remaining()) {

View File

@ -25,83 +25,8 @@
package jdk.internal.jimage; package jdk.internal.jimage;
import java.nio.ByteBuffer; interface ImageStrings {
import java.util.HashMap; public UTF8String get(int offset);
class ImageStrings { public int add(final UTF8String string);
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();
}
} }

View File

@ -22,46 +22,23 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package jdk.internal.jimage; package jdk.internal.jimage;
/** class ImageStringsReader implements ImageStrings {
* Resource is a class or resource file. private final BasicImageReader reader;
*/
public class Resource {
private final String name;
private final long size;
private final long csize;
public Resource(String name, long size, long csize) { ImageStringsReader(BasicImageReader reader) {
this.name = name; this.reader = reader;
this.size = size;
this.csize = csize;
}
/**
* Returns the name of this entry.
*/
public String name() {
return name;
}
/**
* Returns the number of uncompressed bytes for this entry.
*/
public long size() {
return size;
}
/**
* Returns the number of compressed bytes for this entry; 0 if
* uncompressed.
*/
public long csize() {
return csize;
} }
@Override @Override
public String toString() { public UTF8String get(int offset) {
return String.format("%s uncompressed size %d compressed size %d", name, size, csize); return reader.getUTF8String(offset);
}
@Override
public int add(final UTF8String string) {
throw new InternalError("Can not add strings at runtime");
} }
} }

View File

@ -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();
}
}

View File

@ -22,41 +22,24 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package jdk.internal.jimage; package jdk.internal.jimage;
import java.io.IOException; import java.io.Closeable;
import java.nio.ByteBuffer; 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. interface ImageSubstrate extends Closeable {
@Override
public final class PackageModuleMap { void close();
private PackageModuleMap() {} boolean supportsDataBuffer();
ByteBuffer getIndexBuffer(long offset, long size);
public static final String MODULES_ENTRY = "module/modules.offsets"; ByteBuffer getDataBuffer(long offset, long size);
public static final String PACKAGES_ENTRY = "packages.offsets"; boolean read(long offset,
ByteBuffer compressedBuffer, long compressedSize,
/* ByteBuffer uncompressedBuffer, long uncompressedSize);
* Returns a package-to-module map. boolean read(long offset,
* ByteBuffer uncompressedBuffer, long uncompressedSize);
* The package name is in binary name format. byte[] getStringBytes(int offset);
*/ long[] getAttributes(int offset);
static Map<String,String> readFrom(ImageReader reader) throws IOException { ImageLocation findLocation(UTF8String name, ImageStringsReader strings);
Map<String,String> result = new HashMap<>(); int[] attributeOffsets();
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;
}
} }

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}
}
}

View File

@ -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);
}

View File

@ -29,14 +29,18 @@ import java.nio.charset.Charset;
import java.util.Arrays; import java.util.Arrays;
public final class UTF8String implements CharSequence { public final class UTF8String implements CharSequence {
// Same as StandardCharsets.UTF_8 without loading all of the standard charsets // Same as StandardCharsets.UTF_8 without loading all of the standard charsets
static final Charset UTF_8 = Charset.forName("UTF-8"); static final Charset UTF_8 = Charset.forName("UTF-8");
static final int NOT_FOUND = -1; static final int NOT_FOUND = -1;
static final int HASH_MULTIPLIER = 0x01000193; static final int HASH_MULTIPLIER = 0x01000193;
static final UTF8String EMPTY_STRING = new UTF8String(""); static final UTF8String EMPTY_STRING = new UTF8String("");
static final UTF8String CLASS_STRING = new UTF8String(".class"); 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 byte[] bytes;
final int offset; final int offset;
@ -160,8 +164,8 @@ public final class UTF8String implements CharSequence {
return seed & 0x7FFFFFFF; return seed & 0x7FFFFFFF;
} }
int hashCode(int base) { int hashCode(int seed) {
return hashCode(base, bytes, offset, count); return hashCode(seed, bytes, offset, count);
} }
@Override @Override
@ -186,7 +190,7 @@ public final class UTF8String implements CharSequence {
return equals(this, (UTF8String)obj); return equals(this, (UTF8String)obj);
} }
private static boolean equals(UTF8String a, UTF8String b) { public static boolean equals(UTF8String a, UTF8String b) {
if (a == b) { if (a == b) {
return true; return true;
} }
@ -211,6 +215,10 @@ public final class UTF8String implements CharSequence {
return true; return true;
} }
public byte[] getBytesCopy() {
return Arrays.copyOfRange(bytes, offset, offset + count);
}
byte[] getBytes() { byte[] getBytes() {
if (offset != 0 || bytes.length != count) { if (offset != 0 || bytes.length != count) {
return Arrays.copyOfRange(bytes, offset, offset + count); return Arrays.copyOfRange(bytes, offset, offset + count);
@ -232,33 +240,11 @@ public final class UTF8String implements CharSequence {
public char charAt(int index) { public char charAt(int index) {
int ch = byteAt(index); int ch = byteAt(index);
return (ch & 0x80) != 0 ? (char)ch : '\0'; return (ch & 0x80) == 0 ? (char)ch : '\0';
} }
@Override @Override
public CharSequence subSequence(int start, int end) { public CharSequence subSequence(int start, int end) {
return (CharSequence)substring(start, end - start); 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);
}
} }

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -51,7 +51,7 @@ final class JrtDirectoryStream implements DirectoryStream<Path> {
this.jrtfs = jrtPath.getFileSystem(); this.jrtfs = jrtPath.getFileSystem();
this.path = jrtPath.getResolvedPath(); this.path = jrtPath.getResolvedPath();
// sanity check // sanity check
if (!jrtfs.isDirectory(path)) if (!jrtfs.isDirectory(path, true))
throw new NotDirectoryException(jrtPath.toString()); throw new NotDirectoryException(jrtPath.toString());
// absolute path and does not have funky chars in front like /./java.base // absolute path and does not have funky chars in front like /./java.base

View File

@ -25,6 +25,7 @@
package jdk.internal.jrtfs; package jdk.internal.jrtfs;
import java.nio.file.LinkOption;
import java.nio.file.attribute.*; import java.nio.file.attribute.*;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -48,30 +49,32 @@ final class JrtFileAttributeView implements BasicFileAttributeView
private final JrtPath path; private final JrtPath path;
private final boolean isJrtView; 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.path = path;
this.isJrtView = isJrtView; this.isJrtView = isJrtView;
this.options = options;
} }
@SuppressWarnings("unchecked") // Cast to V @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) if (type == null)
throw new NullPointerException(); throw new NullPointerException();
if (type == BasicFileAttributeView.class) if (type == BasicFileAttributeView.class)
return (V)new JrtFileAttributeView(path, false); return (V)new JrtFileAttributeView(path, false, options);
if (type == JrtFileAttributeView.class) if (type == JrtFileAttributeView.class)
return (V)new JrtFileAttributeView(path, true); return (V)new JrtFileAttributeView(path, true, options);
return null; return null;
} }
static JrtFileAttributeView get(JrtPath path, String type) { static JrtFileAttributeView get(JrtPath path, String type, LinkOption... options) {
if (type == null) if (type == null)
throw new NullPointerException(); throw new NullPointerException();
if (type.equals("basic")) if (type.equals("basic"))
return new JrtFileAttributeView(path, false); return new JrtFileAttributeView(path, false, options);
if (type.equals("jjrt")) if (type.equals("jjrt"))
return new JrtFileAttributeView(path, true); return new JrtFileAttributeView(path, true, options);
return null; return null;
} }
@ -83,7 +86,7 @@ final class JrtFileAttributeView implements BasicFileAttributeView
@Override @Override
public JrtFileAttributes readAttributes() throws IOException public JrtFileAttributes readAttributes() throws IOException
{ {
return path.getAttributes(); return path.getAttributes(options);
} }
@Override @Override

View File

@ -76,12 +76,12 @@ final class JrtFileAttributes implements BasicFileAttributes
@Override @Override
public boolean isSymbolicLink() { public boolean isSymbolicLink() {
return false; return node.isLink();
} }
@Override @Override
public Object fileKey() { public Object fileKey() {
return null; return node.resolveLink(true);
} }
///////// jrt entry attributes /////////// ///////// jrt entry attributes ///////////

View File

@ -31,9 +31,9 @@ import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.AccessMode;
import java.nio.file.ClosedFileSystemException; import java.nio.file.ClosedFileSystemException;
import java.nio.file.CopyOption; import java.nio.file.CopyOption;
import java.nio.file.LinkOption;
import java.nio.file.FileStore; import java.nio.file.FileStore;
import java.nio.file.FileSystem; import java.nio.file.FileSystem;
import java.nio.file.FileSystemException; import java.nio.file.FileSystemException;
@ -45,16 +45,13 @@ import java.nio.file.OpenOption;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.PathMatcher; import java.nio.file.PathMatcher;
import java.nio.file.ReadOnlyFileSystemException; import java.nio.file.ReadOnlyFileSystemException;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.nio.file.WatchService; import java.nio.file.WatchService;
import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileTime; import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.UserPrincipalLookupService; import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider; import java.nio.file.spi.FileSystemProvider;
import java.security.AccessController; import java.util.concurrent.ConcurrentHashMap;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -63,8 +60,9 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern; 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;
import jdk.internal.jimage.ImageReader.Node; import jdk.internal.jimage.ImageReader.Node;
import jdk.internal.jimage.UTF8String; import jdk.internal.jimage.UTF8String;
@ -74,6 +72,7 @@ import jdk.internal.jimage.UTF8String;
*/ */
class JrtFileSystem extends FileSystem { class JrtFileSystem extends FileSystem {
private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final Charset UTF_8 = Charset.forName("UTF-8");
private final JrtFileSystemProvider provider; private final JrtFileSystemProvider provider;
// System image readers // System image readers
private ImageReader bootImage; private ImageReader bootImage;
@ -109,7 +108,8 @@ class JrtFileSystem extends FileSystem {
this.extImage = openImage(SystemImages.extImagePath); this.extImage = openImage(SystemImages.extImagePath);
this.appImage = openImage(SystemImages.appImagePath); this.appImage = openImage(SystemImages.appImagePath);
rootPath = new JrtPath(this, new byte[]{'/'}); byte[] root = new byte[] { '/' };
rootPath = new JrtPath(this, root);
isOpen = true; isOpen = true;
} }
@ -149,12 +149,12 @@ class JrtFileSystem extends FileSystem {
synchronized(this) { synchronized(this) {
isOpen = false; isOpen = false;
// close all image readers and null out // close all image reader and null out
bootImage.close(); bootImage.close();
extImage.close();
appImage.close();
bootImage = null; bootImage = null;
extImage.close();
extImage = null; extImage = null;
appImage.close();
appImage = null; appImage = null;
} }
} }
@ -289,21 +289,52 @@ class JrtFileSystem extends FileSystem {
} }
} }
private NodeAndImage findNode(byte[] path) throws IOException { private NodeAndImage lookup(byte[] path) {
ImageReader image = bootImage;
Node node = bootImage.findNode(path); Node node = bootImage.findNode(path);
ImageReader image = bootImage;
if (node == null) { if (node == null) {
image = extImage;
node = extImage.findNode(path); node = extImage.findNode(path);
image = extImage;
} }
if (node == null) { if (node == null) {
image = appImage;
node = appImage.findNode(path); node = appImage.findNode(path);
image = appImage;
} }
if (node == null || node.isHidden()) { return node != null? new NodeAndImage(node, image) : null;
throw new NoSuchFileException(getString(path)); }
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 { private NodeAndImage checkNode(byte[] path) throws IOException {
@ -321,10 +352,28 @@ class JrtFileSystem extends FileSystem {
return ni; 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 // package private helpers
JrtFileAttributes getFileAttributes(byte[] path) JrtFileAttributes getFileAttributes(byte[] path, LinkOption... options)
throws IOException { throws IOException {
NodeAndImage ni = checkNode(path); NodeAndImage ni = checkNode(path);
if (ni.node.isLink() && followLinks(options)) {
return new JrtFileAttributes(ni.node.resolveLink(true));
}
return new JrtFileAttributes(ni.node); return new JrtFileAttributes(ni.node);
} }
@ -343,11 +392,13 @@ class JrtFileSystem extends FileSystem {
return true; return true;
} }
boolean isDirectory(byte[] path) boolean isDirectory(byte[] path, boolean resolveLinks)
throws IOException { throws IOException {
ensureOpen(); ensureOpen();
NodeAndImage ni = checkNode(path); 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) { JrtPath toJrtPath(String path) {
@ -358,6 +409,28 @@ class JrtFileSystem extends FileSystem {
return new JrtPath(this, path); 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" * 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) Iterator<Path> iteratorOf(byte[] path, String childPrefix)
throws IOException { throws IOException {
NodeAndImage ni = checkNode(path); NodeAndImage ni = checkNode(path);
if (!ni.node.isDirectory()) { Node node = ni.node.resolveLink(true);
if (!node.isDirectory()) {
throw new NotDirectoryException(getString(path)); throw new NotDirectoryException(getString(path));
} }
if (ni.node.isRootDir()) { if (node.isRootDir()) {
return rootDirIterator(path, childPrefix); 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) { private Iterator<Path> nodesToIterator(Path path, String childPrefix, List<Node> childNodes) {
List<Path> childPaths; Function<Node, Path> f = childPrefix == null
if (childPrefix == null) { ? child -> toJrtPath(child.getNameString())
childPaths = childNodes.stream() : child -> toJrtPath(childPrefix + child.getNameString().substring(1));
.filter(Node::isVisible) return childNodes.stream().map(f).collect(toList()).iterator();
.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();
} }
private List<Node> rootChildren; private void addRootDirContent(List<Node> children) {
private static void addRootDirContent(List<Node> dest, List<Node> src) { for (Node child : children) {
for (Node n : src) { if (!(child.isModulesDir() || child.isPackagesDir())) {
// only module directories at the top level. Filter other stuff! rootChildren.add(child);
if (n.isModuleDir()) {
dest.add(n);
} }
} }
} }
private List<Node> rootChildren;
private synchronized void initRootChildren(byte[] path) { private synchronized void initRootChildren(byte[] path) {
if (rootChildren == null) { if (rootChildren == null) {
rootChildren = new ArrayList<>(); rootChildren = new ArrayList<>();
addRootDirContent(rootChildren, bootImage.findNode(path).getChildren()); rootChildren.addAll(bootImage.findNode(path).getChildren());
addRootDirContent(rootChildren, extImage.findNode(path).getChildren()); addRootDirContent(extImage.findNode(path).getChildren());
addRootDirContent(rootChildren, appImage.findNode(path).getChildren()); addRootDirContent(appImage.findNode(path).getChildren());
} }
} }
@ -420,6 +517,35 @@ class JrtFileSystem extends FileSystem {
return nodesToIterator(rootPath, childPrefix, rootChildren); 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) void createDirectory(byte[] dir, FileAttribute<?>... attrs)
throws IOException { throws IOException {
throw readOnly(); throw readOnly();

View File

@ -145,6 +145,11 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
toJrtPath(path).checkAccess(modes); toJrtPath(path).checkAccess(modes);
} }
@Override
public Path readSymbolicLink(Path link) throws IOException {
return toJrtPath(link).readSymbolicLink();
}
@Override @Override
public void copy(Path src, Path target, CopyOption... options) public void copy(Path src, Path target, CopyOption... options)
throws IOException throws IOException
@ -169,7 +174,7 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
public <V extends FileAttributeView> V public <V extends FileAttributeView> V
getFileAttributeView(Path path, Class<V> type, LinkOption... options) getFileAttributeView(Path path, Class<V> type, LinkOption... options)
{ {
return JrtFileAttributeView.get(toJrtPath(path), type); return JrtFileAttributeView.get(toJrtPath(path), type, options);
} }
@Override @Override
@ -250,7 +255,7 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
throws IOException throws IOException
{ {
if (type == BasicFileAttributes.class || type == JrtFileAttributes.class) if (type == BasicFileAttributes.class || type == JrtFileAttributes.class)
return (A)toJrtPath(path).getAttributes(); return (A)toJrtPath(path).getAttributes(options);
return null; return null;
} }

View File

@ -55,6 +55,10 @@ final class JrtPath implements Path {
this.path = normalize(path); this.path = normalize(path);
} }
byte[] getName() {
return path;
}
@Override @Override
public JrtPath getRoot() { public JrtPath getRoot() {
if (this.isAbsolute()) if (this.isAbsolute())
@ -140,10 +144,19 @@ final class JrtPath implements Path {
@Override @Override
public JrtPath toRealPath(LinkOption... options) throws IOException { public JrtPath toRealPath(LinkOption... options) throws IOException {
JrtPath realPath = new JrtPath(jrtfs, getResolvedPath()).toAbsolutePath(); JrtPath realPath = new JrtPath(jrtfs, getResolvedPath()).toAbsolutePath();
realPath = JrtFileSystem.followLinks(options)? jrtfs.resolveLink(this) : realPath;
realPath.checkAccess(); realPath.checkAccess();
return realPath; return realPath;
} }
JrtPath readSymbolicLink() throws IOException {
if (! jrtfs.isLink(this)) {
throw new IOException("not a symbolic link");
}
return jrtfs.resolveLink(this);
}
boolean isHidden() { boolean isHidden() {
return false; return false;
} }
@ -638,9 +651,9 @@ final class JrtPath implements Path {
jrtfs.deleteFile(getResolvedPath(), false); 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) if (zfas == null)
throw new NoSuchFileException(toString()); throw new NoSuchFileException(toString());
return zfas; return zfas;
@ -659,7 +672,7 @@ final class JrtPath implements Path {
type = attribute.substring(0, colonPos++); type = attribute.substring(0, colonPos++);
attr = attribute.substring(colonPos); attr = attribute.substring(colonPos);
} }
JrtFileAttributeView view = JrtFileAttributeView.get(this, type); JrtFileAttributeView view = JrtFileAttributeView.get(this, type, options);
if (view == null) if (view == null)
throw new UnsupportedOperationException("view <" + view + "> is not supported"); throw new UnsupportedOperationException("view <" + view + "> is not supported");
view.setAttribute(attr, value); view.setAttribute(attr, value);
@ -685,7 +698,7 @@ final class JrtPath implements Path {
view = attributes.substring(0, colonPos++); view = attributes.substring(0, colonPos++);
attrs = attributes.substring(colonPos); attrs = attributes.substring(colonPos);
} }
JrtFileAttributeView jrtfv = JrtFileAttributeView.get(this, view); JrtFileAttributeView jrtfv = JrtFileAttributeView.get(this, view, options);
if (jrtfv == null) { if (jrtfv == null) {
throw new UnsupportedOperationException("view not supported"); throw new UnsupportedOperationException("view not supported");
} }
@ -706,9 +719,10 @@ final class JrtPath implements Path {
this.getFileSystem() != other.getFileSystem()) this.getFileSystem() != other.getFileSystem())
return false; return false;
this.checkAccess(); this.checkAccess();
((JrtPath)other).checkAccess(); JrtPath path = (JrtPath)other;
return Arrays.equals(this.getResolvedPath(), path.checkAccess();
((JrtPath)other).getResolvedPath()); return Arrays.equals(this.getResolvedPath(), path.getResolvedPath()) ||
jrtfs.isSameFile(this, (JrtPath)other);
} }
SeekableByteChannel newByteChannel(Set<? extends OpenOption> options, SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,

View File

@ -42,6 +42,7 @@ final class SystemImages {
static final Path bootImagePath; static final Path bootImagePath;
static final Path extImagePath; static final Path extImagePath;
static final Path appImagePath; static final Path appImagePath;
static { static {
PrivilegedAction<String> pa = SystemImages::findHome; PrivilegedAction<String> pa = SystemImages::findHome;
RUNTIME_HOME = AccessController.doPrivileged(pa); RUNTIME_HOME = AccessController.doPrivileged(pa);

View File

@ -31,6 +31,8 @@ import java.security.ProtectionDomain;
import sun.reflect.CallerSensitive; import sun.reflect.CallerSensitive;
import sun.reflect.Reflection; import sun.reflect.Reflection;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* A collection of methods for performing low-level, unsafe operations. * 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 * @throws RuntimeException No defined exceptions are thrown, not even
* {@link NullPointerException} * {@link NullPointerException}
*/ */
@HotSpotIntrinsicCandidate
public native int getInt(Object o, long offset); 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 * @throws RuntimeException No defined exceptions are thrown, not even
* {@link NullPointerException} * {@link NullPointerException}
*/ */
@HotSpotIntrinsicCandidate
public native void putInt(Object o, long offset, int x); public native void putInt(Object o, long offset, int x);
/** /**
* Fetches a reference value from a given Java variable. * Fetches a reference value from a given Java variable.
* @see #getInt(Object, long) * @see #getInt(Object, long)
*/ */
@HotSpotIntrinsicCandidate
public native Object getObject(Object o, long offset); public native Object getObject(Object o, long offset);
/** /**
@ -188,35 +193,50 @@ public final class Unsafe {
* are updated. * are updated.
* @see #putInt(Object, long, int) * @see #putInt(Object, long, int)
*/ */
@HotSpotIntrinsicCandidate
public native void putObject(Object o, long offset, Object x); public native void putObject(Object o, long offset, Object x);
/** @see #getInt(Object, long) */ /** @see #getInt(Object, long) */
@HotSpotIntrinsicCandidate
public native boolean getBoolean(Object o, long offset); public native boolean getBoolean(Object o, long offset);
/** @see #putInt(Object, long, int) */ /** @see #putInt(Object, long, int) */
@HotSpotIntrinsicCandidate
public native void putBoolean(Object o, long offset, boolean x); public native void putBoolean(Object o, long offset, boolean x);
/** @see #getInt(Object, long) */ /** @see #getInt(Object, long) */
@HotSpotIntrinsicCandidate
public native byte getByte(Object o, long offset); public native byte getByte(Object o, long offset);
/** @see #putInt(Object, long, int) */ /** @see #putInt(Object, long, int) */
@HotSpotIntrinsicCandidate
public native void putByte(Object o, long offset, byte x); public native void putByte(Object o, long offset, byte x);
/** @see #getInt(Object, long) */ /** @see #getInt(Object, long) */
@HotSpotIntrinsicCandidate
public native short getShort(Object o, long offset); public native short getShort(Object o, long offset);
/** @see #putInt(Object, long, int) */ /** @see #putInt(Object, long, int) */
@HotSpotIntrinsicCandidate
public native void putShort(Object o, long offset, short x); public native void putShort(Object o, long offset, short x);
/** @see #getInt(Object, long) */ /** @see #getInt(Object, long) */
@HotSpotIntrinsicCandidate
public native char getChar(Object o, long offset); public native char getChar(Object o, long offset);
/** @see #putInt(Object, long, int) */ /** @see #putInt(Object, long, int) */
@HotSpotIntrinsicCandidate
public native void putChar(Object o, long offset, char x); public native void putChar(Object o, long offset, char x);
/** @see #getInt(Object, long) */ /** @see #getInt(Object, long) */
@HotSpotIntrinsicCandidate
public native long getLong(Object o, long offset); public native long getLong(Object o, long offset);
/** @see #putInt(Object, long, int) */ /** @see #putInt(Object, long, int) */
@HotSpotIntrinsicCandidate
public native void putLong(Object o, long offset, long x); public native void putLong(Object o, long offset, long x);
/** @see #getInt(Object, long) */ /** @see #getInt(Object, long) */
@HotSpotIntrinsicCandidate
public native float getFloat(Object o, long offset); public native float getFloat(Object o, long offset);
/** @see #putInt(Object, long, int) */ /** @see #putInt(Object, long, int) */
@HotSpotIntrinsicCandidate
public native void putFloat(Object o, long offset, float x); public native void putFloat(Object o, long offset, float x);
/** @see #getInt(Object, long) */ /** @see #getInt(Object, long) */
@HotSpotIntrinsicCandidate
public native double getDouble(Object o, long offset); public native double getDouble(Object o, long offset);
/** @see #putInt(Object, long, int) */ /** @see #putInt(Object, long, int) */
@HotSpotIntrinsicCandidate
public native void putDouble(Object o, long offset, double x); public native void putDouble(Object o, long offset, double x);
// These read VM internal data. // These read VM internal data.
@ -257,6 +277,7 @@ public final class Unsafe {
* *
* @see #allocateMemory * @see #allocateMemory
*/ */
@HotSpotIntrinsicCandidate
public native byte getByte(long address); public native byte getByte(long address);
/** /**
@ -266,31 +287,44 @@ public final class Unsafe {
* *
* @see #getByte(long) * @see #getByte(long)
*/ */
@HotSpotIntrinsicCandidate
public native void putByte(long address, byte x); public native void putByte(long address, byte x);
/** @see #getByte(long) */ /** @see #getByte(long) */
@HotSpotIntrinsicCandidate
public native short getShort(long address); public native short getShort(long address);
/** @see #putByte(long, byte) */ /** @see #putByte(long, byte) */
@HotSpotIntrinsicCandidate
public native void putShort(long address, short x); public native void putShort(long address, short x);
/** @see #getByte(long) */ /** @see #getByte(long) */
@HotSpotIntrinsicCandidate
public native char getChar(long address); public native char getChar(long address);
/** @see #putByte(long, byte) */ /** @see #putByte(long, byte) */
@HotSpotIntrinsicCandidate
public native void putChar(long address, char x); public native void putChar(long address, char x);
/** @see #getByte(long) */ /** @see #getByte(long) */
@HotSpotIntrinsicCandidate
public native int getInt(long address); public native int getInt(long address);
/** @see #putByte(long, byte) */ /** @see #putByte(long, byte) */
@HotSpotIntrinsicCandidate
public native void putInt(long address, int x); public native void putInt(long address, int x);
/** @see #getByte(long) */ /** @see #getByte(long) */
@HotSpotIntrinsicCandidate
public native long getLong(long address); public native long getLong(long address);
/** @see #putByte(long, byte) */ /** @see #putByte(long, byte) */
@HotSpotIntrinsicCandidate
public native void putLong(long address, long x); public native void putLong(long address, long x);
/** @see #getByte(long) */ /** @see #getByte(long) */
@HotSpotIntrinsicCandidate
public native float getFloat(long address); public native float getFloat(long address);
/** @see #putByte(long, byte) */ /** @see #putByte(long, byte) */
@HotSpotIntrinsicCandidate
public native void putFloat(long address, float x); public native void putFloat(long address, float x);
/** @see #getByte(long) */ /** @see #getByte(long) */
@HotSpotIntrinsicCandidate
public native double getDouble(long address); public native double getDouble(long address);
/** @see #putByte(long, byte) */ /** @see #putByte(long, byte) */
@HotSpotIntrinsicCandidate
public native void putDouble(long address, double x); public native void putDouble(long address, double x);
/** /**
@ -307,6 +341,7 @@ public final class Unsafe {
* *
* @see #allocateMemory * @see #allocateMemory
*/ */
@HotSpotIntrinsicCandidate
public native long getAddress(long address); public native long getAddress(long address);
/** /**
@ -319,6 +354,7 @@ public final class Unsafe {
* *
* @see #getAddress(long) * @see #getAddress(long)
*/ */
@HotSpotIntrinsicCandidate
public native void putAddress(long address, long x); public native void putAddress(long address, long x);
/// wrappers for malloc, realloc, free: /// wrappers for malloc, realloc, free:
@ -406,6 +442,7 @@ public final class Unsafe {
* *
* @since 1.7 * @since 1.7
*/ */
@HotSpotIntrinsicCandidate
public native void copyMemory(Object srcBase, long srcOffset, public native void copyMemory(Object srcBase, long srcOffset,
Object destBase, long destOffset, Object destBase, long destOffset,
long bytes); long bytes);
@ -651,6 +688,7 @@ public final class Unsafe {
* Allocates an instance but does not run any constructor. * Allocates an instance but does not run any constructor.
* Initializes the class if it has not yet been. * Initializes the class if it has not yet been.
*/ */
@HotSpotIntrinsicCandidate
public native Object allocateInstance(Class<?> cls) public native Object allocateInstance(Class<?> cls)
throws InstantiationException; throws InstantiationException;
@ -666,6 +704,7 @@ public final class Unsafe {
* *
* @return {@code true} if successful * @return {@code true} if successful
*/ */
@HotSpotIntrinsicCandidate
public final native boolean compareAndSwapObject(Object o, long offset, public final native boolean compareAndSwapObject(Object o, long offset,
Object expected, Object expected,
Object x); Object x);
@ -679,6 +718,7 @@ public final class Unsafe {
* *
* @return {@code true} if successful * @return {@code true} if successful
*/ */
@HotSpotIntrinsicCandidate
public final native boolean compareAndSwapInt(Object o, long offset, public final native boolean compareAndSwapInt(Object o, long offset,
int expected, int expected,
int x); int x);
@ -692,6 +732,7 @@ public final class Unsafe {
* *
* @return {@code true} if successful * @return {@code true} if successful
*/ */
@HotSpotIntrinsicCandidate
public final native boolean compareAndSwapLong(Object o, long offset, public final native boolean compareAndSwapLong(Object o, long offset,
long expected, long expected,
long x); long x);
@ -700,60 +741,78 @@ public final class Unsafe {
* Fetches a reference value from a given Java variable, with volatile * Fetches a reference value from a given Java variable, with volatile
* load semantics. Otherwise identical to {@link #getObject(Object, long)} * load semantics. Otherwise identical to {@link #getObject(Object, long)}
*/ */
@HotSpotIntrinsicCandidate
public native Object getObjectVolatile(Object o, long offset); public native Object getObjectVolatile(Object o, long offset);
/** /**
* Stores a reference value into a given Java variable, with * Stores a reference value into a given Java variable, with
* volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)} * volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)}
*/ */
@HotSpotIntrinsicCandidate
public native void putObjectVolatile(Object o, long offset, Object x); public native void putObjectVolatile(Object o, long offset, Object x);
/** Volatile version of {@link #getInt(Object, long)} */ /** Volatile version of {@link #getInt(Object, long)} */
@HotSpotIntrinsicCandidate
public native int getIntVolatile(Object o, long offset); public native int getIntVolatile(Object o, long offset);
/** Volatile version of {@link #putInt(Object, long, int)} */ /** Volatile version of {@link #putInt(Object, long, int)} */
@HotSpotIntrinsicCandidate
public native void putIntVolatile(Object o, long offset, int x); public native void putIntVolatile(Object o, long offset, int x);
/** Volatile version of {@link #getBoolean(Object, long)} */ /** Volatile version of {@link #getBoolean(Object, long)} */
@HotSpotIntrinsicCandidate
public native boolean getBooleanVolatile(Object o, long offset); public native boolean getBooleanVolatile(Object o, long offset);
/** Volatile version of {@link #putBoolean(Object, long, boolean)} */ /** Volatile version of {@link #putBoolean(Object, long, boolean)} */
@HotSpotIntrinsicCandidate
public native void putBooleanVolatile(Object o, long offset, boolean x); public native void putBooleanVolatile(Object o, long offset, boolean x);
/** Volatile version of {@link #getByte(Object, long)} */ /** Volatile version of {@link #getByte(Object, long)} */
@HotSpotIntrinsicCandidate
public native byte getByteVolatile(Object o, long offset); public native byte getByteVolatile(Object o, long offset);
/** Volatile version of {@link #putByte(Object, long, byte)} */ /** Volatile version of {@link #putByte(Object, long, byte)} */
@HotSpotIntrinsicCandidate
public native void putByteVolatile(Object o, long offset, byte x); public native void putByteVolatile(Object o, long offset, byte x);
/** Volatile version of {@link #getShort(Object, long)} */ /** Volatile version of {@link #getShort(Object, long)} */
@HotSpotIntrinsicCandidate
public native short getShortVolatile(Object o, long offset); public native short getShortVolatile(Object o, long offset);
/** Volatile version of {@link #putShort(Object, long, short)} */ /** Volatile version of {@link #putShort(Object, long, short)} */
@HotSpotIntrinsicCandidate
public native void putShortVolatile(Object o, long offset, short x); public native void putShortVolatile(Object o, long offset, short x);
/** Volatile version of {@link #getChar(Object, long)} */ /** Volatile version of {@link #getChar(Object, long)} */
@HotSpotIntrinsicCandidate
public native char getCharVolatile(Object o, long offset); public native char getCharVolatile(Object o, long offset);
/** Volatile version of {@link #putChar(Object, long, char)} */ /** Volatile version of {@link #putChar(Object, long, char)} */
@HotSpotIntrinsicCandidate
public native void putCharVolatile(Object o, long offset, char x); public native void putCharVolatile(Object o, long offset, char x);
/** Volatile version of {@link #getLong(Object, long)} */ /** Volatile version of {@link #getLong(Object, long)} */
@HotSpotIntrinsicCandidate
public native long getLongVolatile(Object o, long offset); public native long getLongVolatile(Object o, long offset);
/** Volatile version of {@link #putLong(Object, long, long)} */ /** Volatile version of {@link #putLong(Object, long, long)} */
@HotSpotIntrinsicCandidate
public native void putLongVolatile(Object o, long offset, long x); public native void putLongVolatile(Object o, long offset, long x);
/** Volatile version of {@link #getFloat(Object, long)} */ /** Volatile version of {@link #getFloat(Object, long)} */
@HotSpotIntrinsicCandidate
public native float getFloatVolatile(Object o, long offset); public native float getFloatVolatile(Object o, long offset);
/** Volatile version of {@link #putFloat(Object, long, float)} */ /** Volatile version of {@link #putFloat(Object, long, float)} */
@HotSpotIntrinsicCandidate
public native void putFloatVolatile(Object o, long offset, float x); public native void putFloatVolatile(Object o, long offset, float x);
/** Volatile version of {@link #getDouble(Object, long)} */ /** Volatile version of {@link #getDouble(Object, long)} */
@HotSpotIntrinsicCandidate
public native double getDoubleVolatile(Object o, long offset); public native double getDoubleVolatile(Object o, long offset);
/** Volatile version of {@link #putDouble(Object, long, double)} */ /** Volatile version of {@link #putDouble(Object, long, double)} */
@HotSpotIntrinsicCandidate
public native void putDoubleVolatile(Object o, long offset, double x); 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). * Corresponds to C11 atomic_store_explicit(..., memory_order_release).
*/ */
@HotSpotIntrinsicCandidate
public native void putOrderedObject(Object o, long offset, Object x); public native void putOrderedObject(Object o, long offset, Object x);
/** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */ /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */
@HotSpotIntrinsicCandidate
public native void putOrderedInt(Object o, long offset, int x); public native void putOrderedInt(Object o, long offset, int x);
/** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */ /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */
@HotSpotIntrinsicCandidate
public native void putOrderedLong(Object o, long offset, long x); public native void putOrderedLong(Object o, long offset, long x);
/** /**
@ -785,6 +847,7 @@ public final class Unsafe {
* *
* @param thread the thread to unpark. * @param thread the thread to unpark.
*/ */
@HotSpotIntrinsicCandidate
public native void unpark(Object thread); 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 * because {@code unpark} is, so it would be strange to place it
* elsewhere. * elsewhere.
*/ */
@HotSpotIntrinsicCandidate
public native void park(boolean isAbsolute, long time); public native void park(boolean isAbsolute, long time);
/** /**
@ -831,6 +895,7 @@ public final class Unsafe {
* @return the previous value * @return the previous value
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) { public final int getAndAddInt(Object o, long offset, int delta) {
int v; int v;
do { do {
@ -850,6 +915,7 @@ public final class Unsafe {
* @return the previous value * @return the previous value
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public final long getAndAddLong(Object o, long offset, long delta) { public final long getAndAddLong(Object o, long offset, long delta) {
long v; long v;
do { do {
@ -869,6 +935,7 @@ public final class Unsafe {
* @return the previous value * @return the previous value
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public final int getAndSetInt(Object o, long offset, int newValue) { public final int getAndSetInt(Object o, long offset, int newValue) {
int v; int v;
do { do {
@ -888,6 +955,7 @@ public final class Unsafe {
* @return the previous value * @return the previous value
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public final long getAndSetLong(Object o, long offset, long newValue) { public final long getAndSetLong(Object o, long offset, long newValue) {
long v; long v;
do { do {
@ -907,6 +975,7 @@ public final class Unsafe {
* @return the previous value * @return the previous value
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public final Object getAndSetObject(Object o, long offset, Object newValue) { public final Object getAndSetObject(Object o, long offset, Object newValue) {
Object v; Object v;
do { do {
@ -928,6 +997,7 @@ public final class Unsafe {
* provide a LoadLoad barrier also provide a LoadStore barrier for free. * provide a LoadLoad barrier also provide a LoadStore barrier for free.
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public native void loadFence(); public native void loadFence();
/** /**
@ -942,6 +1012,7 @@ public final class Unsafe {
* provide a StoreStore barrier also provide a LoadStore barrier for free. * provide a StoreStore barrier also provide a LoadStore barrier for free.
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public native void storeFence(); public native void storeFence();
/** /**
@ -953,6 +1024,7 @@ public final class Unsafe {
* Corresponds to C11 atomic_thread_fence(memory_order_seq_cst). * Corresponds to C11 atomic_thread_fence(memory_order_seq_cst).
* @since 1.8 * @since 1.8
*/ */
@HotSpotIntrinsicCandidate
public native void fullFence(); public native void fullFence();
/** /**
@ -1010,6 +1082,7 @@ public final class Unsafe {
* {@link NullPointerException} * {@link NullPointerException}
* @since 1.9 * @since 1.9
*/ */
@HotSpotIntrinsicCandidate
public final long getLongUnaligned(Object o, long offset) { public final long getLongUnaligned(Object o, long offset) {
if ((offset & 7) == 0) { if ((offset & 7) == 0) {
return getLong(o, offset); return getLong(o, offset);
@ -1048,6 +1121,7 @@ public final class Unsafe {
} }
/** @see #getLongUnaligned(Object, long) */ /** @see #getLongUnaligned(Object, long) */
@HotSpotIntrinsicCandidate
public final int getIntUnaligned(Object o, long offset) { public final int getIntUnaligned(Object o, long offset) {
if ((offset & 3) == 0) { if ((offset & 3) == 0) {
return getInt(o, offset); return getInt(o, offset);
@ -1067,6 +1141,7 @@ public final class Unsafe {
} }
/** @see #getLongUnaligned(Object, long) */ /** @see #getLongUnaligned(Object, long) */
@HotSpotIntrinsicCandidate
public final short getShortUnaligned(Object o, long offset) { public final short getShortUnaligned(Object o, long offset) {
if ((offset & 1) == 0) { if ((offset & 1) == 0) {
return getShort(o, offset); return getShort(o, offset);
@ -1081,9 +1156,11 @@ public final class Unsafe {
} }
/** @see #getLongUnaligned(Object, long) */ /** @see #getLongUnaligned(Object, long) */
@HotSpotIntrinsicCandidate
public final char getCharUnaligned(Object o, long offset) { public final char getCharUnaligned(Object o, long offset) {
return (char)getShortUnaligned(o, offset); return (char)getShortUnaligned(o, offset);
} }
/** @see #getLongUnaligned(Object, long, boolean) */ /** @see #getLongUnaligned(Object, long, boolean) */
public final char getCharUnaligned(Object o, long offset, boolean bigEndian) { public final char getCharUnaligned(Object o, long offset, boolean bigEndian) {
return convEndian(bigEndian, getCharUnaligned(o, offset)); return convEndian(bigEndian, getCharUnaligned(o, offset));
@ -1117,6 +1194,7 @@ public final class Unsafe {
* {@link NullPointerException} * {@link NullPointerException}
* @since 1.9 * @since 1.9
*/ */
@HotSpotIntrinsicCandidate
public final void putLongUnaligned(Object o, long offset, long x) { public final void putLongUnaligned(Object o, long offset, long x) {
if ((offset & 7) == 0) { if ((offset & 7) == 0) {
putLong(o, offset, x); putLong(o, offset, x);
@ -1142,6 +1220,7 @@ public final class Unsafe {
(byte)(x >>> 56)); (byte)(x >>> 56));
} }
} }
/** /**
* As {@link #putLongUnaligned(Object, long, long)} but with an additional * As {@link #putLongUnaligned(Object, long, long)} but with an additional
* argument which specifies the endianness of the value as stored in memory. * 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) */ /** @see #putLongUnaligned(Object, long, long) */
@HotSpotIntrinsicCandidate
public final void putIntUnaligned(Object o, long offset, int x) { public final void putIntUnaligned(Object o, long offset, int x) {
if ((offset & 3) == 0) { if ((offset & 3) == 0) {
putInt(o, offset, x); putInt(o, offset, x);
@ -1179,6 +1259,7 @@ public final class Unsafe {
} }
/** @see #putLongUnaligned(Object, long, long) */ /** @see #putLongUnaligned(Object, long, long) */
@HotSpotIntrinsicCandidate
public final void putShortUnaligned(Object o, long offset, short x) { public final void putShortUnaligned(Object o, long offset, short x) {
if ((offset & 1) == 0) { if ((offset & 1) == 0) {
putShort(o, offset, x); putShort(o, offset, x);
@ -1194,6 +1275,7 @@ public final class Unsafe {
} }
/** @see #putLongUnaligned(Object, long, long) */ /** @see #putLongUnaligned(Object, long, long) */
@HotSpotIntrinsicCandidate
public final void putCharUnaligned(Object o, long offset, char x) { public final void putCharUnaligned(Object o, long offset, char x) {
putShortUnaligned(o, offset, (short)x); putShortUnaligned(o, offset, (short)x);
} }

View File

@ -32,6 +32,9 @@ import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder; import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult; import java.nio.charset.CoderResult;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
class ISO_8859_1 class ISO_8859_1
extends Charset extends Charset
@ -147,9 +150,16 @@ class ISO_8859_1
private final Surrogate.Parser sgp = new Surrogate.Parser(); 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, 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; int i = 0;
for (; i < len; i++) { for (; i < len; i++) {
@ -161,6 +171,34 @@ class ISO_8859_1
return i; 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, private CoderResult encodeArrayLoop(CharBuffer src,
ByteBuffer dst) ByteBuffer dst)
{ {

View File

@ -28,6 +28,7 @@ package sun.reflect;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import jdk.internal.HotSpotIntrinsicCandidate;
/** Common utility routines used by both java.lang and /** Common utility routines used by both java.lang and
java.lang.reflect */ java.lang.reflect */
@ -56,6 +57,7 @@ public class Reflection {
ignoring frames associated with java.lang.reflect.Method.invoke() ignoring frames associated with java.lang.reflect.Method.invoke()
and its implementation. */ and its implementation. */
@CallerSensitive @CallerSensitive
@HotSpotIntrinsicCandidate
public static native Class<?> getCallerClass(); public static native Class<?> getCallerClass();
/** /**
@ -74,6 +76,7 @@ public class Reflection {
to compatibility reasons; see 4471811. Only the values of the to compatibility reasons; see 4471811. Only the values of the
low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
valid. */ valid. */
@HotSpotIntrinsicCandidate
public static native int getClassAccessFlags(Class<?> c); public static native int getClassAccessFlags(Class<?> c);
/** A quick "fast-path" check to try to avoid getCallerClass() /** A quick "fast-path" check to try to avoid getCallerClass()

View File

@ -28,6 +28,9 @@ package sun.security.provider;
import java.security.MessageDigestSpi; import java.security.MessageDigestSpi;
import java.security.DigestException; import java.security.DigestException;
import java.security.ProviderException; import java.security.ProviderException;
import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* Common base message digest implementation for the Sun provider. * Common base message digest implementation for the Sun provider.
@ -136,12 +139,36 @@ abstract class DigestBase extends MessageDigestSpi implements Cloneable {
// compress complete blocks // compress complete blocks
private int implCompressMultiBlock(byte[] b, int ofs, int limit) { 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) { for (; ofs <= limit; ofs += blockSize) {
implCompress(b, ofs); implCompress(b, ofs);
} }
return 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. // reset this object. See JCA doc.
protected final void engineReset() { protected final void engineReset() {
if (bytesProcessed == 0) { if (bytesProcessed == 0) {

View File

@ -25,7 +25,10 @@
package sun.security.provider; package sun.security.provider;
import java.util.Objects;
import static sun.security.provider.ByteArrayAccess.*; import static sun.security.provider.ByteArrayAccess.*;
import jdk.internal.HotSpotIntrinsicCandidate;
/** /**
* This class implements the Secure Hash Algorithm (SHA) developed by * 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. * "old" NIST Secure Hash Algorithm.
*/ */
void implCompress(byte[] buf, int ofs) { 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 first 16 ints have the byte stream, compute the rest of
// the buffer // the buffer
for (int t = 16; t <= 79; t++) { for (int t = 16; t <= 79; t++) {

View File

@ -25,6 +25,9 @@
package sun.security.provider; package sun.security.provider;
import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate;
import static sun.security.provider.ByteArrayAccess.*; 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. * Process the current block to update the state variable state.
*/ */
void implCompress(byte[] buf, int ofs) { 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 first 16 ints are from the byte stream, compute the rest of
// the W[]'s // the W[]'s
for (int t = 16; t < ITERATION; t++) { for (int t = 16; t < ITERATION; t++) {

View File

@ -26,8 +26,10 @@
package sun.security.provider; package sun.security.provider;
import java.security.*; import java.security.*;
import java.util.Objects;
import java.math.BigInteger; import java.math.BigInteger;
import jdk.internal.HotSpotIntrinsicCandidate;
import static sun.security.provider.ByteArrayAccess.*; import static sun.security.provider.ByteArrayAccess.*;
/** /**
@ -205,8 +207,27 @@ abstract class SHA5 extends DigestBase {
* "old" NIST Secure Hash Algorithm. * "old" NIST Secure Hash Algorithm.
*/ */
final void implCompress(byte[] buf, int ofs) { 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 first 16 longs are from the byte stream, compute the rest of
// the W[]'s // the W[]'s
for (int t = 16; t < ITERATION; t++) { for (int t = 16; t < ITERATION; t++) {

View File

@ -556,6 +556,48 @@ JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused);
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
JVM_SupportsCX8(void); 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 * com.sun.dtrace.jsdt support
*/ */

View 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;
}

View File

@ -43,8 +43,8 @@ Java_java_util_zip_CRC32_update(JNIEnv *env, jclass cls, jint crc, jint b)
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_java_util_zip_CRC32_updateBytes(JNIEnv *env, jclass cls, jint crc, Java_java_util_zip_CRC32_updateBytes0(JNIEnv *env, jclass cls, jint crc,
jarray b, jint off, jint len) jarray b, jint off, jint len)
{ {
Bytef *buf = (*env)->GetPrimitiveArrayCritical(env, b, 0); Bytef *buf = (*env)->GetPrimitiveArrayCritical(env, b, 0);
if (buf) { if (buf) {
@ -61,8 +61,8 @@ ZIP_CRC32(jint crc, const jbyte *buf, jint len)
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_java_util_zip_CRC32_updateByteBuffer(JNIEnv *env, jclass cls, jint crc, Java_java_util_zip_CRC32_updateByteBuffer0(JNIEnv *env, jclass cls, jint crc,
jlong address, jint off, jint len) jlong address, jint off, jint len)
{ {
Bytef *buf = (Bytef *)jlong_to_ptr(address); Bytef *buf = (Bytef *)jlong_to_ptr(address);
if (buf) { if (buf) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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, '/');
}
}

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,141 +25,98 @@
package jdk.tools.jimage; package jdk.tools.jimage;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter; 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.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.text.MessageFormat; import static java.nio.file.StandardOpenOption.READ;
import java.util.ArrayList; import static java.nio.file.StandardOpenOption.WRITE;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; 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.BasicImageReader;
import jdk.internal.jimage.BasicImageWriter;
import jdk.internal.jimage.ImageHeader; 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.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 { 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) { static final Option<?>[] recognizedOptions = {
super(JImageTask.getMessage(key, args)); new Option<JImageTask>(true, "--dir") {
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") {
@Override @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; task.options.directory = arg;
} }
}, },
new HiddenOption(false, "--fullversion") { new HiddenOption<JImageTask>(false, "--fullversion") {
@Override @Override
void process(JImageTask task, String opt, String arg) { protected void process(JImageTask task, String opt, String arg) {
task.options.fullVersion = true; task.options.fullVersion = true;
} }
}, },
new Option(false, "--help") { new Option<JImageTask>(false, "--help") {
@Override @Override
void process(JImageTask task, String opt, String arg) { protected void process(JImageTask task, String opt, String arg) {
task.options.help = true; task.options.help = true;
} }
}, },
new Option(false, "--verbose") {
new Option<JImageTask>(true, "--flags") {
@Override @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; task.options.verbose = true;
} }
}, },
new Option(false, "--version") { new Option<JImageTask>(false, "--version") {
@Override @Override
void process(JImageTask task, String opt, String arg) { protected void process(JImageTask task, String opt, String arg) {
task.options.version = true; 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; Task task = Task.LIST;
String directory = "."; String directory = ".";
boolean fullVersion; boolean fullVersion;
boolean help; boolean help;
String flags;
boolean verbose; boolean verbose;
boolean version; boolean version;
List<File> jimages = new LinkedList<>(); List<File> jimages = new LinkedList<>();
} }
private static final String PROGNAME = "jimage"; private static final String PROGNAME = "jimage";
private final Options options = new Options(); private final OptionsValues options = new OptionsValues();
enum Task { enum Task {
RECREATE,
EXTRACT, EXTRACT,
INFO, INFO,
LIST, LIST,
RECREATE,
SET,
VERIFY VERIFY
}; };
@ -210,23 +167,29 @@ class JImageTask {
int run(String[] args) { int run(String[] args) {
if (log == null) { if (log == null) {
log = new PrintWriter(System.out); setLog(new PrintWriter(System.out));
} }
try { 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) { if (options.help) {
showHelp(); optionsHelper.showHelp(PROGNAME, "recreate only options:");
} }
if (options.version || options.fullVersion) { if (options.version || options.fullVersion) {
showVersion(options.fullVersion); taskHelper.showVersion(options.fullVersion);
} }
boolean ok = run(); boolean ok = run();
return ok ? EXIT_OK : EXIT_ERROR; return ok ? EXIT_OK : EXIT_ERROR;
} catch (BadArgs e) { } catch (BadArgs e) {
reportError(e.key, e.args); taskHelper.reportError(e.key, e.args);
if (e.showUsage) { if (e.showUsage) {
log.println(getMessage("main.usage.summary", PROGNAME)); log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
} }
return EXIT_CMDERR; return EXIT_CMDERR;
} catch (Exception x) { } 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 { private void recreate() throws IOException, BadArgs {
File directory = new File(options.directory); File directory = new File(options.directory);
Path dirPath = directory.toPath();
int chop = dirPath.toString().length() + 1;
if (!directory.isDirectory()) { 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()) { if (options.jimages.isEmpty()) {
throw new BadArgs("err.jimage.not.specified"); throw taskHelper.newBadArgs("err.jimage.not.specified");
} else if (options.jimages.size() != 1) { } 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); Path jimage = options.jimages.get(0).toPath();
final List<File> files = new ArrayList<>();
final BasicImageWriter writer = new BasicImageWriter();
final Long longZero = 0L;
// Note: code sensitive to Netbeans parser crashing. if (jimage.toFile().createNewFile()) {
long total = Files.walk(dirPath).reduce(longZero, (Long offset, Path path) -> { ExtractedImage img = new ExtractedImage(dirPath, log, options.verbose);
long size = 0; img.recreateJImage(jimage);
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());
}
}
}
} else { } 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) { private void title(File file, BasicImageReader reader) {
@ -351,10 +242,12 @@ class JImageTask {
} }
private interface ResourceAction { 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); File directory = new File(options.directory);
byte[] bytes = reader.getResource(location); byte[] bytes = reader.getResource(location);
File resource = new File(directory, name); File resource = new File(directory, name);
@ -362,21 +255,23 @@ class JImageTask {
if (parent.exists()) { if (parent.exists()) {
if (!parent.isDirectory()) { 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()) { } 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)) { if (name.endsWith(ImageModuleData.META_DATA_EXTENSION)) {
List<String> names = reader.getNames(bytes); ImageModuleData imageModuleData = new ImageModuleData(reader, bytes);
Files.write(resource.toPath(), names); List<String> lines = imageModuleData.fromModulePackages();
Files.write(resource.toPath(), lines);
} else { } 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 NUMBER_WIDTH = 12;
private static final int OFFSET_WIDTH = NUMBER_WIDTH; private static final int OFFSET_WIDTH = NUMBER_WIDTH;
private static final int SIZE_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(); ImageHeader header = reader.getHeader();
log.println(" Major Version: " + header.getMajorVersion()); log.println(" Major Version: " + header.getMajorVersion());
log.println(" Minor Version: " + header.getMinorVersion()); 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(" Offsets Size: " + header.getOffsetsSize());
log.println(" Redirects Size: " + header.getRedirectSize()); log.println(" Redirects Size: " + header.getRedirectSize());
log.println(" Locations Size: " + header.getLocationsSize()); log.println(" Locations Size: " + header.getLocationsSize());
@ -414,16 +311,39 @@ class JImageTask {
print(reader, name); print(reader, name);
} }
void verify(BasicImageReader reader, String name, ImageLocation location) { void set(File file, BasicImageReader reader) throws BadArgs {
if (name.endsWith(".class")) { try {
byte[] bytes; ImageHeader oldHeader = reader.getHeader();
int value = 0;
try { try {
bytes = reader.getResource(location); value = Integer.valueOf(options.flags);
} catch (IOException ex) { } catch (NumberFormatException ex) {
log.println(ex); throw taskHelper.newBadArgs("err.flags.not.int", options.flags);
bytes = null;
} }
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 || if (bytes == null || bytes.length <= 4 ||
(bytes[0] & 0xFF) != 0xCA || (bytes[0] & 0xFF) != 0xCA ||
(bytes[1] & 0xFF) != 0xFE || (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) { for (File file : options.jimages) {
if (!file.exists() || !file.isFile()) { 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(); String path = file.getCanonicalPath();
@ -449,11 +370,13 @@ class JImageTask {
} }
if (resourceAction != null) { if (resourceAction != null) {
String[] entryNames = reader.getEntryNames(true); String[] entryNames = reader.getEntryNames();
for (String name : entryNames) { for (String name : entryNames) {
ImageLocation location = reader.findLocation(name); if (!ImageResourcesTree.isTreeInfoResource(name)) {
resourceAction.apply(reader, name, location); ImageLocation location = reader.findLocation(name);
resourceAction.apply(reader, name, location);
}
} }
} }
} }
@ -461,9 +384,6 @@ class JImageTask {
private boolean run() throws IOException, BadArgs { private boolean run() throws IOException, BadArgs {
switch (options.task) { switch (options.task) {
case RECREATE:
recreate();
break;
case EXTRACT: case EXTRACT:
iterate(null, this::extract); iterate(null, this::extract);
break; break;
@ -473,11 +393,17 @@ class JImageTask {
case LIST: case LIST:
iterate(this::listTitle, this::list); iterate(this::listTitle, this::list);
break; break;
case RECREATE:
recreate();
break;
case SET:
iterate(this::set, null);
break;
case VERIFY: case VERIFY:
iterate(this::title, this::verify); iterate(this::title, this::verify);
break; break;
default: 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; return true;
} }
@ -485,112 +411,6 @@ class JImageTask {
private PrintWriter log; private PrintWriter log;
void setLog(PrintWriter out) { void setLog(PrintWriter out) {
log = out; log = out;
} taskHelper.setLog(log);
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);
}
}
} }
} }

View 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");
}
}

View File

@ -1,16 +1,17 @@
main.usage.summary=\ 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 use --help for a list of possible options
main.usage=\ main.usage=\
Usage: {0} <extract|recreate|info|list|verify> <options> jimage...\n\ Usage: {0} <extract|info|list|recreate|set|verify> <options> jimage...\n\
\n\ \n\
\ extract - Extract all jimage entries into separate files into the directory\n\ \ extract - Extract all jimage entries into separate files into the directory\n\
\ specified by --dir=<directory> (default='.')\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\ \ info - Prints information specified in the jimage header.\n\
\ list - Prints the names of all the entries in the jimage. When used with\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\ \ --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\ \ verify - Reports errors on any .class entries that don't verify as classes.\n\
\n\ \n\
Possible options include: Possible options include:
@ -19,27 +20,32 @@ error.prefix=Error:
warn.prefix=Warning: warn.prefix=Warning:
main.opt.dir=\ main.opt.dir=\
\ --dir Target directory for create/expand \ --dir Target directory for extract/recreate
main.opt.verbose=\ main.opt.flags=\
\ --verbose Verbose listing \ --flags=value Set the jimage flags to value
main.opt.help=\ main.opt.help=\
\ --help Print this usage message \ --help Print this usage message
main.opt.verbose=\
\ --verbose Verbose listing
main.opt.version=\ main.opt.version=\
\ --version Version information \ --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.cannot.create.dir=cannot create directory: {0}
err.not.a.jimage=not a jimage file: {0} err.cannot.read.file=cannot read file: {0}
err.unknown.option=unknown option: {0} err.cannot.update.file=cannot update file: {0}
err.missing.arg=no value given for {0} err.flags.not.int=--flags value not integer: {0}
err.internal.error=internal error: {0} {1} {2} err.internal.error=internal error: {0} {1} {2}
err.invalid.arg.for.option=invalid argument for option: {0} 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.option.unsupported={0} not supported: {1}
err.unknown.option=unknown option: {0}

View File

@ -394,7 +394,7 @@ final class JrtClassPathEntry extends ClassPathEntry {
this.pkgDirs = new HashMap<>(); this.pkgDirs = new HashMap<>();
// fill in module directories at the root dir // fill in module directories at the root dir
Path root = fs.getPath("/"); Path root = fs.getPath("/modules");
try { try {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) { try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) {
for (Path entry: stream) { for (Path entry: stream) {

Some files were not shown because too many files have changed in this diff Show More