1 /* 2 * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javac.platform; 27 28 import java.io.IOException; 29 import java.io.PrintWriter; 30 import java.net.URI; 31 import java.nio.charset.Charset; 32 import java.nio.file.DirectoryStream; 33 import java.nio.file.FileSystem; 34 import java.nio.file.FileSystems; 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.nio.file.Paths; 38 import java.nio.file.ProviderNotFoundException; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Collections; 42 import java.util.Comparator; 43 import java.util.EnumSet; 44 import java.util.HashMap; 45 import java.util.Iterator; 46 import java.util.List; 47 import java.util.Map; 48 import java.util.Map.Entry; 49 import java.util.NoSuchElementException; 50 import java.util.Set; 51 import java.util.TreeSet; 52 import java.util.stream.Collectors; 53 import java.util.stream.Stream; 54 55 import javax.annotation.processing.Processor; 56 import javax.tools.ForwardingJavaFileObject; 57 import javax.tools.JavaFileManager; 58 import javax.tools.JavaFileManager.Location; 59 import javax.tools.JavaFileObject; 60 import javax.tools.JavaFileObject.Kind; 61 import javax.tools.StandardJavaFileManager; 62 import javax.tools.StandardLocation; 63 64 import com.sun.source.util.Plugin; 65 import com.sun.tools.javac.code.Source; 66 import com.sun.tools.javac.code.Source.Feature; 67 import com.sun.tools.javac.file.CacheFSInfo; 68 import com.sun.tools.javac.file.JavacFileManager; 69 import com.sun.tools.javac.jvm.Target; 70 import com.sun.tools.javac.util.Context; 71 import com.sun.tools.javac.util.Log; 72 import com.sun.tools.javac.util.StringUtils; 73 74 /** PlatformProvider for JDK N. 75 * 76 * <p><b>This is NOT part of any supported API. 77 * If you write code that depends on this, you do so at your own risk. 78 * This code and its internal interfaces are subject to change or 79 * deletion without notice.</b> 80 */ 81 public class JDKPlatformProvider implements PlatformProvider { 82 83 @Override 84 public Iterable<String> getSupportedPlatformNames() { 85 return SUPPORTED_JAVA_PLATFORM_VERSIONS; 86 } 87 88 @Override 89 public PlatformDescription getPlatform(String platformName, String options) { 90 return new PlatformDescriptionImpl(platformName); 91 } 92 93 private static final String[] symbolFileLocation = { "lib", "ct.sym" }; 94 95 private static final Set<String> SUPPORTED_JAVA_PLATFORM_VERSIONS; 96 public static final Comparator<String> NUMERICAL_COMPARATOR = (s1, s2) -> { 97 int i1; 98 try { 99 i1 = Integer.parseInt(s1); 100 } catch (NumberFormatException ex) { 101 i1 = Integer.MAX_VALUE; 102 } 103 int i2; 104 try { 105 i2 = Integer.parseInt(s2); 106 } catch (NumberFormatException ex) { 107 i2 = Integer.MAX_VALUE; 108 } 109 return i1 != i2 ? i1 - i2 : s1.compareTo(s2); 110 }; 111 112 static { 113 SUPPORTED_JAVA_PLATFORM_VERSIONS = new TreeSet<>(NUMERICAL_COMPARATOR); 114 Path ctSymFile = findCtSym(); 115 if (Files.exists(ctSymFile)) { 116 try (FileSystem fs = FileSystems.newFileSystem(ctSymFile, null); 117 DirectoryStream<Path> dir = 118 Files.newDirectoryStream(fs.getRootDirectories().iterator().next())) { 119 for (Path section : dir) { 120 if (section.getFileName().toString().contains("-")) 121 continue; 122 for (char ver : section.getFileName().toString().toCharArray()) { 123 String verString = Character.toString(ver); 124 Target t = Target.lookup("" + Integer.parseInt(verString, 16)); 125 126 if (t != null) { 127 SUPPORTED_JAVA_PLATFORM_VERSIONS.add(targetNumericVersion(t)); 128 } 129 } 130 } 131 } catch (IOException | ProviderNotFoundException ex) { 132 } 133 } 134 } 135 136 private static String targetNumericVersion(Target target) { 137 return Integer.toString(target.ordinal() - Target.JDK1_1.ordinal() + 1); 138 } 139 140 static class PlatformDescriptionImpl implements PlatformDescription { 141 142 private final Map<Path, FileSystem> ctSym2FileSystem = new HashMap<>(); 143 private final String sourceVersion; 144 private final String ctSymVersion; 145 146 PlatformDescriptionImpl(String sourceVersion) { 147 this.sourceVersion = sourceVersion; 148 this.ctSymVersion = 149 StringUtils.toUpperCase(Integer.toHexString(Integer.parseInt(sourceVersion))); 150 } 151 152 @Override 153 public JavaFileManager getFileManager() { 154 Context context = new Context(); 155 PrintWriter pw = new PrintWriter(System.err, true); 156 context.put(Log.errKey, pw); 157 CacheFSInfo.preRegister(context); 158 JavacFileManager fm = new JavacFileManager(context, true, null) { 159 @Override 160 public boolean hasLocation(Location location) { 161 return super.hasExplicitLocation(location); 162 } 163 164 @Override 165 public JavaFileObject getJavaFileForInput(Location location, String className, 166 Kind kind) throws IOException { 167 if (kind == Kind.CLASS) { 168 String fileName = className.replace('.', '/'); 169 JavaFileObject result = 170 (JavaFileObject) getFileForInput(location, 171 "", 172 fileName + ".sig"); 173 174 if (result == null) { 175 //in jrt://, the classfile may have the .class extension: 176 result = (JavaFileObject) getFileForInput(location, 177 "", 178 fileName + ".class"); 179 } 180 181 if (result != null) { 182 return new SigJavaFileObject(result); 183 } else { 184 return null; 185 } 186 } 187 188 return super.getJavaFileForInput(location, className, kind); 189 } 190 191 @Override 192 public Iterable<JavaFileObject> list(Location location, 193 String packageName, 194 Set<Kind> kinds, 195 boolean recurse) throws IOException { 196 Set<Kind> enhancedKinds = EnumSet.copyOf(kinds); 197 198 enhancedKinds.add(Kind.OTHER); 199 200 Iterable<JavaFileObject> listed = super.list(location, packageName, 201 enhancedKinds, recurse); 202 203 return () -> new Iterator<JavaFileObject>() { 204 private final Iterator<JavaFileObject> original = listed.iterator(); 205 private JavaFileObject next; 206 @Override 207 public boolean hasNext() { 208 if (next == null) { 209 while (original.hasNext()) { 210 JavaFileObject fo = original.next(); 211 212 if (fo.getKind() == Kind.OTHER && 213 fo.getName().endsWith(".sig")) { 214 next = new SigJavaFileObject(fo); 215 break; 216 } 217 218 if (kinds.contains(fo.getKind())) { 219 next = fo; 220 break; 221 } 222 } 223 } 224 return next != null; 225 } 226 227 @Override 228 public JavaFileObject next() { 229 if (!hasNext()) 230 throw new NoSuchElementException(); 231 JavaFileObject result = next; 232 next = null; 233 return result; 234 } 235 236 }; 237 } 238 239 @Override 240 public String inferBinaryName(Location location, JavaFileObject file) { 241 if (file instanceof SigJavaFileObject) { 242 file = ((SigJavaFileObject) file).getDelegate(); 243 } 244 return super.inferBinaryName(location, file); 245 } 246 247 }; 248 249 Path file = findCtSym(); 250 // file == ${jdk.home}/lib/ct.sym 251 if (Files.exists(file)) { 252 try { 253 FileSystem fs = ctSym2FileSystem.get(file); 254 if (fs == null) { 255 ctSym2FileSystem.put(file, fs = FileSystems.newFileSystem(file, null)); 256 } 257 258 Path root = fs.getRootDirectories().iterator().next(); 259 boolean hasModules = 260 Feature.MODULES.allowedInSource(Source.lookup(sourceVersion)); 261 Path systemModules = root.resolve(ctSymVersion).resolve("system-modules"); 262 Charset utf8 = Charset.forName("UTF-8"); 263 264 if (!hasModules) { 265 List<Path> paths = new ArrayList<>(); 266 267 try (DirectoryStream<Path> dir = Files.newDirectoryStream(root)) { 268 for (Path section : dir) { 269 if (section.getFileName().toString().contains(ctSymVersion) && 270 !section.getFileName().toString().contains("-")) { 271 try (DirectoryStream<Path> modules = Files.newDirectoryStream(section)) { 272 for (Path module : modules) { 273 paths.add(module); 274 } 275 } 276 } 277 } 278 } 279 280 fm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, paths); 281 } else if (Files.isRegularFile(systemModules)) { 282 fm.handleOption("--system", Arrays.asList("none").iterator()); 283 284 Path jrtModules = 285 FileSystems.getFileSystem(URI.create("jrt:/")) 286 .getPath("modules"); 287 try (Stream<String> lines = 288 Files.lines(systemModules, utf8)) { 289 lines.map(line -> jrtModules.resolve(line)) 290 .filter(mod -> Files.exists(mod)) 291 .forEach(mod -> setModule(fm, mod)); 292 } 293 } else { 294 Map<String, List<Path>> module2Paths = new HashMap<>(); 295 296 try (DirectoryStream<Path> dir = Files.newDirectoryStream(root)) { 297 for (Path section : dir) { 298 if (section.getFileName().toString().contains(ctSymVersion) && 299 !section.getFileName().toString().contains("-")) { 300 try (DirectoryStream<Path> modules = Files.newDirectoryStream(section)) { 301 for (Path module : modules) { 302 module2Paths.computeIfAbsent(module.getFileName().toString(), dummy -> new ArrayList<>()).add(module); 303 } 304 } 305 } 306 } 307 } 308 309 fm.handleOption("--system", Arrays.asList("none").iterator()); 310 311 for (Entry<String, List<Path>> e : module2Paths.entrySet()) { 312 fm.setLocationForModule(StandardLocation.SYSTEM_MODULES, 313 e.getKey(), 314 e.getValue()); 315 } 316 } 317 318 return fm; 319 } catch (IOException ex) { 320 throw new IllegalStateException(ex); 321 } 322 } else { 323 throw new IllegalStateException("Cannot find ct.sym!"); 324 } 325 } 326 327 private static void setModule(StandardJavaFileManager fm, Path mod) { 328 try { 329 fm.setLocationForModule(StandardLocation.SYSTEM_MODULES, 330 mod.getFileName().toString(), 331 Collections.singleton(mod)); 332 } catch (IOException ex) { 333 throw new IllegalStateException(ex); 334 } 335 } 336 337 private static class SigJavaFileObject extends ForwardingJavaFileObject<JavaFileObject> { 338 339 public SigJavaFileObject(JavaFileObject fileObject) { 340 super(fileObject); 341 } 342 343 @Override 344 public Kind getKind() { 345 return Kind.CLASS; 346 } 347 348 @Override 349 public boolean isNameCompatible(String simpleName, Kind kind) { 350 return super.isNameCompatible(simpleName + ".sig", Kind.OTHER); 351 } 352 353 public JavaFileObject getDelegate() { 354 return fileObject; 355 } 356 } 357 358 @Override 359 public String getSourceVersion() { 360 return sourceVersion; 361 } 362 363 @Override 364 public String getTargetVersion() { 365 return sourceVersion; 366 } 367 368 @Override 369 public List<PluginInfo<Processor>> getAnnotationProcessors() { 370 return Collections.emptyList(); 371 } 372 373 @Override 374 public List<PluginInfo<Plugin>> getPlugins() { 375 return Collections.emptyList(); 376 } 377 378 @Override 379 public List<String> getAdditionalOptions() { 380 return Collections.emptyList(); 381 } 382 383 @Override 384 public void close() throws IOException { 385 for (FileSystem fs : ctSym2FileSystem.values()) { 386 fs.close(); 387 } 388 ctSym2FileSystem.clear(); 389 } 390 391 } 392 393 static Path findCtSym() { 394 String javaHome = System.getProperty("java.home"); 395 Path file = Paths.get(javaHome); 396 // file == ${jdk.home} 397 for (String name : symbolFileLocation) 398 file = file.resolve(name); 399 return file; 400 } 401 402 }