Copyright (c) 2019 IBM Corporation. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v20.html Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2019 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v20.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.jdt.internal.compiler.batch; import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.file.DirectoryStream; import java.nio.file.FileSystemNotFoundException; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.function.Function; import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; import org.eclipse.jdt.internal.compiler.env.IModule; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.util.JRTUtil; import org.eclipse.jdt.internal.compiler.util.Util; public class ClasspathJep247Jdk12 extends ClasspathJep247 { Map<String, IModule> modules; static String MODULE_INFO = "module-info.sig"; //$NON-NLS-1$ public ClasspathJep247Jdk12(File jdkHome, String release, AccessRuleSet accessRuleSet) { super(jdkHome, release, accessRuleSet); } @Override public List<Classpath> fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) { return null; } @Override public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName) { return findClass(typeName, qualifiedPackageName, moduleName, qualifiedBinaryFileName, false); } @Override public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName, boolean asBinaryOnly) { if (!isPackage(qualifiedPackageName, moduleName)) return null; // most common case try { ClassFileReader reader = null; byte[] content = null; qualifiedBinaryFileName = qualifiedBinaryFileName.replace(".class", ".sig"); //$NON-NLS-1$ //$NON-NLS-2$ if (this.subReleases != null && this.subReleases.length > 0) { done: for (String rel : this.subReleases) { if (moduleName == null) { Path p = this.fs.getPath(rel); try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(p)) { for (final java.nio.file.Path subdir: stream) { Path f = this.fs.getPath(rel, JRTUtil.sanitizedFileName(subdir), qualifiedBinaryFileName); if (Files.exists(f)) { content = JRTUtil.safeReadBytes(f); if (content != null) break done; } } } } else { Path p = this.fs.getPath(rel, moduleName, qualifiedBinaryFileName); if (Files.exists(p)) { content = JRTUtil.safeReadBytes(p); if (content != null) break; } } } } else { content = JRTUtil.safeReadBytes(this.fs.getPath(this.releaseInHex, qualifiedBinaryFileName)); } if (content != null) { reader = new ClassFileReader(content, qualifiedBinaryFileName.toCharArray()); char[] modName = moduleName != null ? moduleName.toCharArray() : null; return new NameEnvironmentAnswer(reader, fetchAccessRestriction(qualifiedBinaryFileName), modName); } } catch (ClassFormatException | IOException e) { // continue } return null; } @Override public void initialize() throws IOException { if (this.compliance == null) { return; } if (this.fs != null) { super.initialize(); return; } this.releaseInHex = Integer.toHexString(Integer.parseInt(this.compliance)).toUpperCase(); Path filePath = this.jdkHome.toPath().resolve("lib").resolve("ct.sym"); //$NON-NLS-1$ //$NON-NLS-2$ URI t = filePath.toUri(); if (!Files.exists(filePath)) { return; } URI uri = URI.create("jar:file:" + t.getRawPath()); //$NON-NLS-1$ try { this.fs = FileSystems.getFileSystem(uri); } catch(FileSystemNotFoundException fne) { // Ignore and move on } if (this.fs == null) { HashMap<String, ?> env = new HashMap<>(); this.fs = FileSystems.newFileSystem(uri, env); } this.releasePath = this.fs.getPath("/"); //$NON-NLS-1$ if (!Files.exists(this.fs.getPath(this.releaseInHex))) { throw new IllegalArgumentException("release " + this.compliance + " is not found in the system"); //$NON-NLS-1$//$NON-NLS-2$ } List<String> sub = new ArrayList<>(); try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(this.releasePath)) { for (final java.nio.file.Path subdir: stream) { String rel = JRTUtil.sanitizedFileName(subdir); if (rel.contains(this.releaseInHex)) sub.add(rel); } this.subReleases = sub.toArray(new String[sub.size()]); } catch (IOException e) { //e.printStackTrace(); } super.initialize(); } @Override public void loadModules() { // Modules below level 8 are not dealt with here. Leave it to ClasspathJrt if (this.jdklevel <= ClassFileConstants.JDK1_8) { super.loadModules(); return; } final Path modPath = this.fs.getPath(this.releaseInHex); this.modulePath = this.file.getPath() + "|" + modPath.toString(); //$NON-NLS-1$ this.modules = ModulesCache.get(this.modulePath); if (this.modules == null) { try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(this.releasePath)) { HashMap<String,IModule> newCache = new HashMap<>(); for (final java.nio.file.Path subdir: stream) { String rel = JRTUtil.sanitizedFileName(subdir); if (!rel.contains(this.releaseInHex)) { continue; } Files.walkFileTree(subdir, Collections.EMPTY_SET, 2, new FileVisitor<java.nio.file.Path>() { @Override public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs) throws IOException { if (attrs.isDirectory() || f.getNameCount() < 3) return FileVisitResult.CONTINUE; if (f.getFileName().toString().equals(MODULE_INFO) && Files.exists(f)) { byte[] content = JRTUtil.safeReadBytes(f); if (content == null) return FileVisitResult.CONTINUE; Path m = f.subpath(1, f.getNameCount() - 1); String name = JRTUtil.sanitizedFileName(m); ClasspathJep247Jdk12.this.acceptModule(name, content, newCache); ClasspathJep247Jdk12.this.moduleNamesCache.add(name); } return FileVisitResult.SKIP_SIBLINGS; } @Override public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } }); } synchronized(ModulesCache) { if (ModulesCache.get(this.modulePath) == null) { this.modules = Collections.unmodifiableMap(newCache); ModulesCache.put(this.modulePath, this.modules); } } } catch (IOException e) { e.printStackTrace(); } } else { this.moduleNamesCache.addAll(this.modules.keySet()); } } @Override public Collection<String> getModuleNames(Collection<String> limitModule, Function<String, IModule> getModule) { return selectModules(this.moduleNamesCache, limitModule, getModule); } @Override public IModule getModule(char[] moduleName) { if (this.modules != null) { return this.modules.get(String.valueOf(moduleName)); } return null; } void acceptModule(String name, byte[] content, Map<String, IModule> cache) { if (content == null) return; if (cache.containsKey(name)) return; ClassFileReader reader = null; try { reader = new ClassFileReader(content, IModule.MODULE_INFO_CLASS.toCharArray()); } catch (ClassFormatException e) { e.printStackTrace(); } if (reader != null) { acceptModule(reader, cache); } } @Override void acceptModule(ClassFileReader reader, Map<String, IModule> cache) { // Modules below level 8 are not dealt with here. Leave it to ClasspathJrt if (this.jdklevel <= ClassFileConstants.JDK1_8) { super.acceptModule(reader, cache); return; } if (reader != null) { IModule moduleDecl = reader.getModuleDeclaration(); if (moduleDecl != null) { cache.put(String.valueOf(moduleDecl.name()), moduleDecl); } } } @Override public synchronized char[][] getModulesDeclaringPackage(String qualifiedPackageName, String moduleName) { // Ignore moduleName as this has nothing to do with modules (as of now) if (this.packageCache != null) return singletonModuleNameIf(this.packageCache.contains(qualifiedPackageName)); this.packageCache = new HashSet<>(41); this.packageCache.add(Util.EMPTY_STRING); try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(this.releasePath)) { for (final java.nio.file.Path subdir: stream) { String rel = JRTUtil.sanitizedFileName(subdir); if (!rel.contains(this.releaseInHex)) { continue; } try (DirectoryStream<java.nio.file.Path> stream2 = Files.newDirectoryStream(subdir)) { for (final java.nio.file.Path subdir2: stream2) { Files.walkFileTree(subdir2, new FileVisitor<java.nio.file.Path>() { @Override public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException { if (dir.getNameCount() <= 2) return FileVisitResult.CONTINUE; Path relative = dir.subpath(2, dir.getNameCount()); addToPackageCache(relative.toString(), false); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } }); } } } } catch (IOException e) { e.printStackTrace(); // Rethrow } return singletonModuleNameIf(this.packageCache.contains(qualifiedPackageName)); } }