< prev index next >

src/java.base/share/classes/jdk/internal/jimage/ImageReader.java

Print this page

        

*** 24,72 **** */ package jdk.internal.jimage; import java.io.IOException; import java.io.UncheckedIOException; - import java.net.URI; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.file.Files; - import java.nio.file.FileSystem; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.nio.file.Paths; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; ! import java.util.function.Supplier; public class ImageReader extends BasicImageReader { // well-known strings needed for image file system. ! static final UTF8String ROOT = new UTF8String("/"); ! static final UTF8String META_INF = new UTF8String("/META-INF"); ! static final UTF8String PACKAGES_OFFSETS = new UTF8String("packages.offsets"); // attributes of the .jimage file. jimage file does not contain // attributes for the individual resources (yet). We use attributes // of the jimage file itself (creation, modification, access times). // Iniitalized lazily, see {@link #imageFileAttributes()}. private BasicFileAttributes imageFileAttributes; ! private final Map<String, String> packageMap; // directory management implementation private final Map<UTF8String, Node> nodes; private volatile Directory rootDir; ImageReader(String imagePath, ByteOrder byteOrder) throws IOException { super(imagePath, byteOrder); ! this.packageMap = PackageModuleMap.readFrom(this); this.nodes = Collections.synchronizedMap(new HashMap<>()); } ImageReader(String imagePath) throws IOException { this(imagePath, ByteOrder.nativeOrder()); --- 24,71 ---- */ package jdk.internal.jimage; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.nio.file.Paths; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; ! import static jdk.internal.jimage.UTF8String.*; public class ImageReader extends BasicImageReader { // well-known strings needed for image file system. ! static final UTF8String ROOT_STRING = UTF8String.SLASH_STRING; // attributes of the .jimage file. jimage file does not contain // attributes for the individual resources (yet). We use attributes // of the jimage file itself (creation, modification, access times). // Iniitalized lazily, see {@link #imageFileAttributes()}. private BasicFileAttributes imageFileAttributes; ! private final ImageModuleData moduleData; // directory management implementation private final Map<UTF8String, Node> nodes; private volatile Directory rootDir; + private Directory packagesDir; + private Directory modulesDir; + ImageReader(String imagePath, ByteOrder byteOrder) throws IOException { super(imagePath, byteOrder); ! this.moduleData = new ImageModuleData(this); this.nodes = Collections.synchronizedMap(new HashMap<>()); } ImageReader(String imagePath) throws IOException { this(imagePath, ByteOrder.nativeOrder());
*** 87,174 **** public synchronized void close() throws IOException { super.close(); clearNodes(); } /** * Return the module name that contains the given package name. */ ! public String getModule(String pkg) { ! return packageMap.get(pkg); } // jimage file does not store directory structure. We build nodes // using the "path" strings found in the jimage file. // Node can be a directory or a resource public static abstract class Node { private static final int ROOT_DIR = 0b0000_0000_0000_0001; ! private static final int MODULE_DIR = 0b0000_0000_0000_0010; ! private static final int METAINF_DIR = 0b0000_0000_0000_0100; ! private static final int TOPLEVEL_PKG_DIR = 0b0000_0000_0000_1000; ! private static final int HIDDEN = 0b0000_0000_0001_0000; private int flags; private final UTF8String name; private final BasicFileAttributes fileAttrs; Node(UTF8String name, BasicFileAttributes fileAttrs) { assert name != null; assert fileAttrs != null; this.name = name; this.fileAttrs = fileAttrs; } ! public final void setIsRootDir() { ! flags |= ROOT_DIR; } ! public final boolean isRootDir() { ! return (flags & ROOT_DIR) != 0; } ! public final void setIsModuleDir() { ! flags |= MODULE_DIR; } ! public final boolean isModuleDir() { ! return (flags & MODULE_DIR) != 0; } ! public final void setIsMetaInfDir() { ! flags |= METAINF_DIR; } ! public final boolean isMetaInfDir() { ! return (flags & METAINF_DIR) != 0; } ! public final void 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() { ! return name; } ! public final BasicFileAttributes getFileAttributes() { ! return fileAttrs; } public boolean isDirectory() { return false; } --- 86,210 ---- public synchronized void close() throws IOException { super.close(); clearNodes(); } + @Override + public ImageLocation findLocation(UTF8String name) { + ImageLocation location = super.findLocation(name); + + // NOTE: This should be removed when module system is up in full. + if (location == null) { + int index = name.lastIndexOf('/'); + + if (index != -1) { + UTF8String packageName = name.substring(0, index); + UTF8String moduleName = moduleData.packageToModule(packageName); + + if (moduleName != null) { + UTF8String fullName = UTF8String.SLASH_STRING.concat(moduleName, + UTF8String.SLASH_STRING, name); + location = super.findLocation(fullName); + } + } else { + // No package, try all modules. + for (String mod : moduleData.allModuleNames()) { + location = super.findLocation("/" + mod + "/" + name); + if (location != null) { + break; + } + } + } + } + + return location; + } + /** * Return the module name that contains the given package name. */ ! public String getModule(String packageName) { ! return moduleData.packageToModule(packageName); } // jimage file does not store directory structure. We build nodes // using the "path" strings found in the jimage file. // Node can be a directory or a resource public static abstract class Node { private static final int ROOT_DIR = 0b0000_0000_0000_0001; ! private static final int PACKAGES_DIR = 0b0000_0000_0000_0010; ! private static final int MODULES_DIR = 0b0000_0000_0000_0100; private int flags; private final UTF8String name; private final BasicFileAttributes fileAttrs; + private boolean completed; Node(UTF8String name, BasicFileAttributes fileAttrs) { assert name != null; assert fileAttrs != null; this.name = name; this.fileAttrs = fileAttrs; } ! /** ! * A node is completed when all its direct children have been built. ! * ! * @return ! */ ! public boolean isCompleted() { ! return completed; } ! public void setCompleted(boolean completed) { ! this.completed = completed; } ! public final void setIsRootDir() { ! flags |= ROOT_DIR; } ! public final boolean isRootDir() { ! return (flags & ROOT_DIR) != 0; } ! public final void setIsPackagesDir() { ! flags |= PACKAGES_DIR; } ! public final boolean isPackagesDir() { ! return (flags & PACKAGES_DIR) != 0; } ! public final void setIsModulesDir() { ! flags |= MODULES_DIR; } ! public final boolean isModulesDir() { ! return (flags & MODULES_DIR) != 0; } ! public final UTF8String getName() { ! return name; } ! public final BasicFileAttributes getFileAttributes() { ! return fileAttrs; } ! // resolve this Node (if this is a soft link, get underlying Node) ! public final Node resolveLink() { ! return resolveLink(false); } ! public Node resolveLink(boolean recursive) { ! return this; } ! // is this a soft link Node? ! public boolean isLink() { ! return false; } public boolean isDirectory() { return false; }
*** 240,266 **** return false; } } // directory node - directory has full path name without '/' at end. ! public static final class Directory extends Node { private final List<Node> children; ! @SuppressWarnings("LeakingThisInConstructor") ! Directory(Directory parent, UTF8String name, BasicFileAttributes fileAttrs) { super(name, fileAttrs); children = new ArrayList<>(); if (parent != null) { ! parent.addChild(this); } } @Override public boolean isDirectory() { return true; } public List<Node> getChildren() { return Collections.unmodifiableList(children); } void addChild(Node node) { --- 276,307 ---- return false; } } // directory node - directory has full path name without '/' at end. ! static final class Directory extends Node { private final List<Node> children; ! private Directory(Directory parent, UTF8String name, BasicFileAttributes fileAttrs) { super(name, fileAttrs); children = new ArrayList<>(); + } + + static Directory create(Directory parent, UTF8String name, BasicFileAttributes fileAttrs) { + Directory dir = new Directory(parent, name, fileAttrs); if (parent != null) { ! parent.addChild(dir); } + return dir; } @Override public boolean isDirectory() { return true; } + @Override public List<Node> getChildren() { return Collections.unmodifiableList(children); } void addChild(Node node) {
*** 279,301 **** } } // "resource" is .class or any other resource (compressed/uncompressed) in a jimage. // full path of the resource is the "name" of the resource. ! public static class Resource extends Node { private final ImageLocation loc; ! @SuppressWarnings("LeakingThisInConstructor") ! Resource(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) { ! this(parent, ROOT.concat(loc.getFullname()), loc, fileAttrs); } ! @SuppressWarnings("LeakingThisInConstructor") ! Resource(Directory parent, UTF8String name, ImageLocation loc, BasicFileAttributes fileAttrs) { super(name, fileAttrs); this.loc = loc; ! parent.addChild(this); } @Override public boolean isResource() { return true; --- 320,356 ---- } } // "resource" is .class or any other resource (compressed/uncompressed) in a jimage. // full path of the resource is the "name" of the resource. ! static class Resource extends Node { private final ImageLocation loc; ! private Resource(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) { ! this(parent, loc.getFullName(true), loc, fileAttrs); } ! private Resource(Directory parent, UTF8String name, ImageLocation loc, BasicFileAttributes fileAttrs) { super(name, fileAttrs); this.loc = loc; ! } ! ! 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 public boolean isResource() { return true;
*** 325,334 **** --- 380,420 ---- public long contentOffset() { return loc.getContentOffset(); } } + // represents a soft link to another Node + static class LinkNode extends Node { + private final Node link; + + private LinkNode(Directory parent, UTF8String name, Node link) { + super(name, link.getFileAttributes()); + this.link = link; + } + + static LinkNode create(Directory parent, UTF8String name, Node link) { + LinkNode linkNode = new LinkNode(parent, name, link); + parent.addChild(linkNode); + return linkNode; + } + + @Override + public boolean isCompleted() { + return true; + } + + @Override + public Node resolveLink(boolean recursive) { + return recursive && (link instanceof LinkNode)? ((LinkNode)link).resolveLink(true) : link; + } + + @Override + public boolean isLink() { + return true; + } + } + // directory management interface public Directory getRootDirectory() { return buildRootDirectory(); }
*** 338,350 **** public Node findNode(byte[] name) { return findNode(new UTF8String(name)); } public synchronized Node findNode(UTF8String name) { buildRootDirectory(); ! return nodes.get(name); } private synchronized void clearNodes() { nodes.clear(); rootDir = null; --- 424,581 ---- public Node findNode(byte[] 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) { buildRootDirectory(); ! Node n = nodes.get(name); ! if (n == null || !n.isCompleted()) { ! NodeBuilder builder = new NodeBuilder(name); ! n = builder.buildNode(); ! } ! return n; } private synchronized void clearNodes() { nodes.clear(); rootDir = null;
*** 373,494 **** } // FIXME no time information per resource in jimage file (yet?) // we use file attributes of jimage itself. // root directory ! rootDir = new Directory(null, ROOT, imageFileAttributes()); rootDir.setIsRootDir(); - nodes.put(rootDir.getName(), rootDir); - - ImageLocation[] locs = getAllLocations(true); - for (ImageLocation loc : locs) { - UTF8String parent = loc.getParent(); - // directory where this location goes as child - Directory dir; - if (parent == null || parent.isEmpty()) { - // top level entry under root - dir = rootDir; - } else { - int idx = parent.lastIndexOf('/'); - assert idx != -1 : "invalid parent string"; - UTF8String name = ROOT.concat(parent.substring(0, idx)); - dir = (Directory) nodes.get(name); - if (dir == null) { - // make all parent directories (as needed) - dir = makeDirectories(parent); - } - } - Resource entry = new Resource(dir, loc, imageFileAttributes()); - nodes.put(entry.getName(), entry); - } ! Node metaInf = nodes.get(META_INF); ! if (metaInf instanceof Directory) { ! metaInf.setIsMetaInfDir(); ! ((Directory)metaInf).walk(Node::setIsHidden); ! } ! ! fillPackageModuleInfo(); return rootDir; } private Directory newDirectory(Directory parent, UTF8String name) { ! Directory dir = new Directory(parent, name, imageFileAttributes()); nodes.put(dir.getName(), dir); return dir; } ! private Directory makeDirectories(UTF8String parent) { ! assert !parent.isEmpty() : "non empty parent expected"; ! ! int idx = parent.indexOf('/'); ! assert idx != -1 : "invalid parent string"; ! UTF8String name = ROOT.concat(parent.substring(0, idx)); ! Directory top = (Directory) nodes.get(name); ! if (top == null) { ! top = newDirectory(rootDir, name); ! } ! Directory last = top; ! while ((idx = parent.indexOf('/', idx + 1)) != -1) { ! name = ROOT.concat(parent.substring(0, idx)); ! Directory nextDir = (Directory) nodes.get(name); ! if (nextDir == null) { ! nextDir = newDirectory(last, name); ! } ! last = nextDir; } ! return last; } ! private void fillPackageModuleInfo() { ! assert rootDir != null; ! packageMap.entrySet().stream().sorted((x, y)->x.getKey().compareTo(y.getKey())).forEach((entry) -> { ! UTF8String moduleName = new UTF8String("/" + entry.getValue()); ! UTF8String fullName = moduleName.concat(new UTF8String(entry.getKey() + "/")); ! if (! nodes.containsKey(fullName)) { ! Directory module = (Directory) nodes.get(moduleName); ! assert module != null : "module directory missing " + moduleName; ! module.setIsModuleDir(); ! ! // hide "packages.offsets" in module directories ! Node packagesOffsets = nodes.get(moduleName.concat(ROOT, PACKAGES_OFFSETS)); ! if (packagesOffsets != null) { ! packagesOffsets.setIsHidden(); ! } ! ! // package name without front '/' ! UTF8String pkgName = new UTF8String(entry.getKey() + "/"); ! int idx = -1; ! Directory moduleSubDir = module; ! while ((idx = pkgName.indexOf('/', idx + 1)) != -1) { ! UTF8String subPkg = pkgName.substring(0, idx); ! UTF8String moduleSubDirName = moduleName.concat(ROOT, subPkg); ! Directory tmp = (Directory) nodes.get(moduleSubDirName); ! if (tmp == null) { ! moduleSubDir = newDirectory(moduleSubDir, moduleSubDirName); ! } else { ! moduleSubDir = tmp; } } ! // copy pkgDir "resources" ! Directory pkgDir = (Directory) nodes.get(ROOT.concat(pkgName.substring(0, pkgName.length() - 1))); ! pkgDir.setIsTopLevelPackageDir(); ! pkgDir.walk(n -> n.setIsHidden()); ! for (Node child : pkgDir.getChildren()) { ! if (child.isResource()) { ! ImageLocation loc = child.getLocation(); ! BasicFileAttributes imageFileAttrs = child.getFileAttributes(); ! UTF8String rsName = moduleName.concat(child.getName()); ! Resource rs = new Resource(moduleSubDir, rsName, loc, imageFileAttrs); ! nodes.put(rs.getName(), rs); } } } ! }); } public byte[] getResource(Node node) throws IOException { if (node.isResource()) { return super.getResource(node.getLocation()); --- 604,673 ---- } // FIXME no time information per resource in jimage file (yet?) // we use file attributes of jimage itself. // root directory ! rootDir = newDirectory(null, ROOT_STRING); rootDir.setIsRootDir(); ! // /packages dir ! packagesDir = newDirectory(rootDir, PACKAGES_STRING); ! packagesDir.setIsPackagesDir(); ! ! // /modules dir ! modulesDir = newDirectory(rootDir, MODULES_STRING); ! modulesDir.setIsModulesDir(); + rootDir.setCompleted(true); return rootDir; } private Directory newDirectory(Directory parent, UTF8String name) { ! Directory dir = Directory.create(parent, name, imageFileAttributes()); nodes.put(dir.getName(), dir); return dir; } ! private Resource newResource(Directory parent, ImageLocation loc) { ! Resource res = Resource.create(parent, loc, imageFileAttributes()); ! nodes.put(res.getName(), res); ! return res; } ! private LinkNode newLinkNode(Directory dir, UTF8String name, Node link) { ! LinkNode linkNode = LinkNode.create(dir, name, link); ! nodes.put(linkNode.getName(), linkNode); ! return linkNode; } ! private List<UTF8String> dirs(UTF8String parent) { ! List<UTF8String> splits = new ArrayList<>(); ! for (int i = 1; i < parent.length(); i++) { ! if (parent.byteAt(i) == '/') { ! splits.add(parent.substring(0, i)); } } ! ! splits.add(parent); ! ! return splits; } + + private Directory makeDirectories(UTF8String parent) { + Directory last = rootDir; + List<UTF8String> dirs = dirs(parent); + + for (UTF8String dir : dirs) { + Directory nextDir = (Directory) nodes.get(dir); + if (nextDir == null) { + nextDir = newDirectory(last, dir); } + last = nextDir; } ! ! return last; } public byte[] getResource(Node node) throws IOException { if (node.isResource()) { return super.getResource(node.getLocation());
< prev index next >