1 /* 2 * Copyright (c) 2012, 2014, 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 package com.sun.tools.jdeps; 26 27 import com.sun.tools.classfile.Annotation; 28 import com.sun.tools.classfile.ClassFile; 29 import com.sun.tools.classfile.ConstantPool; 30 import com.sun.tools.classfile.ConstantPoolException; 31 import com.sun.tools.classfile.RuntimeAnnotations_attribute; 32 import com.sun.tools.classfile.Dependencies.ClassFileError; 33 import java.io.IOException; 34 import java.nio.file.FileVisitResult; 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.nio.file.Paths; 38 import java.nio.file.SimpleFileVisitor; 39 import java.nio.file.attribute.BasicFileAttributes; 40 import java.util.*; 41 42 import static com.sun.tools.classfile.Attribute.*; 43 44 /** 45 * ClassPath for Java SE and JDK 46 */ 47 class PlatformClassPath { 48 private static final List<String> NON_PLATFORM_JARFILES = 49 Arrays.asList("alt-rt.jar", "ant-javafx.jar", "javafx-mx.jar"); 50 private static final List<Archive> javaHomeArchives = init(); 51 52 static List<Archive> getArchives() { 53 return javaHomeArchives; 54 } 55 56 private static List<Archive> init() { 57 List<Archive> result = new ArrayList<>(); 58 Path home = Paths.get(System.getProperty("java.home")); 59 try { 60 if (home.endsWith("jre")) { 61 // jar files in <javahome>/jre/lib 62 result.addAll(addJarFiles(home.resolve("lib"))); 63 if (home.getParent() != null) { 64 // add tools.jar and other JDK jar files 65 Path lib = home.getParent().resolve("lib"); 66 if (Files.exists(lib)) { 67 result.addAll(addJarFiles(lib)); 68 } 69 } 70 } else if (Files.exists(home.resolve("lib"))) { 71 // either a JRE or a jdk build image 72 Path classes = home.resolve("classes"); 73 if (Files.isDirectory(classes)) { 74 // jdk build outputdir 75 result.add(new JDKArchive(classes)); 76 } 77 // add other JAR files 78 result.addAll(addJarFiles(home.resolve("lib"))); 79 } else { 80 throw new RuntimeException("\"" + home + "\" not a JDK home"); 81 } 82 return result; 83 } catch (IOException e) { 84 throw new Error(e); 85 } 86 } 87 88 private static List<Archive> addJarFiles(final Path root) throws IOException { 89 final List<Archive> result = new ArrayList<>(); 90 final Path ext = root.resolve("ext"); 91 Files.walkFileTree(root, new SimpleFileVisitor<Path>() { 92 @Override 93 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) 94 throws IOException 95 { 96 if (dir.equals(root) || dir.equals(ext)) { 97 return FileVisitResult.CONTINUE; 98 } else { 99 // skip other cobundled JAR files 100 return FileVisitResult.SKIP_SUBTREE; 101 } 102 } 103 @Override 104 public FileVisitResult visitFile(Path p, BasicFileAttributes attrs) 105 throws IOException 106 { 107 String fn = p.getFileName().toString(); 108 if (fn.endsWith(".jar")) { 109 // JDK may cobundle with JavaFX that doesn't belong to any profile 110 // Treat jfxrt.jar as regular Archive 111 result.add(NON_PLATFORM_JARFILES.contains(fn) 112 ? Archive.getInstance(p) 113 : new JDKArchive(p)); 114 } 115 return FileVisitResult.CONTINUE; 116 } 117 }); 118 return result; 119 } 120 121 /** 122 * A JDK archive is part of the JDK containing the Java SE API 123 * or implementation classes (i.e. JDK internal API) 124 */ 125 static class JDKArchive extends Archive { 126 private static List<String> PROFILE_JARS = Arrays.asList("rt.jar", "jce.jar"); 127 // Workaround: The following packages are not annotated as jdk.Exported 128 private static List<String> EXPORTED_PACKAGES = Arrays.asList( 129 "javax.jnlp", 130 "org.w3c.dom.css", 131 "org.w3c.dom.html", 132 "org.w3c.dom.stylesheets", 133 "org.w3c.dom.xpath" 134 ); 135 public static boolean isProfileArchive(Archive archive) { 136 if (archive instanceof JDKArchive) { 137 return PROFILE_JARS.contains(archive.getName()); 138 } 139 return false; 140 } 141 142 private final Map<String,Boolean> exportedPackages = new HashMap<>(); 143 private final Map<String,Boolean> exportedTypes = new HashMap<>(); 144 JDKArchive(Path p) throws IOException { 145 super(p, ClassFileReader.newInstance(p)); 146 } 147 148 /** 149 * Tests if a given fully-qualified name is an exported type. 150 */ 151 public boolean isExported(String cn) { 152 int i = cn.lastIndexOf('.'); 153 String pn = i > 0 ? cn.substring(0, i) : ""; 154 155 boolean isJdkExported = isExportedPackage(pn); 156 if (exportedTypes.containsKey(cn)) { 157 return exportedTypes.get(cn); 158 } 159 return isJdkExported; 160 } 161 162 /** 163 * Tests if a given package name is exported. 164 */ 165 public boolean isExportedPackage(String pn) { 166 if (Profile.getProfile(pn) != null) { 167 return true; 168 } 169 // special case for JavaFX and APIs that are not annotated with @jdk.Exported) 170 if (EXPORTED_PACKAGES.contains(pn) || pn.startsWith("javafx.")) { 171 return true; 172 } 173 return exportedPackages.containsKey(pn) ? exportedPackages.get(pn) : false; 174 } 175 176 private static final String JDK_EXPORTED_ANNOTATION = "Ljdk/Exported;"; 177 private Boolean isJdkExported(ClassFile cf) throws ConstantPoolException { 178 RuntimeAnnotations_attribute attr = (RuntimeAnnotations_attribute) 179 cf.attributes.get(RuntimeVisibleAnnotations); 180 if (attr != null) { 181 for (int i = 0; i < attr.annotations.length; i++) { 182 Annotation ann = attr.annotations[i]; 183 String annType = cf.constant_pool.getUTF8Value(ann.type_index); 184 if (JDK_EXPORTED_ANNOTATION.equals(annType)) { 185 boolean isJdkExported = true; 186 for (int j = 0; j < ann.num_element_value_pairs; j++) { 187 Annotation.element_value_pair pair = ann.element_value_pairs[j]; 188 Annotation.Primitive_element_value ev = (Annotation.Primitive_element_value) pair.value; 189 ConstantPool.CONSTANT_Integer_info info = (ConstantPool.CONSTANT_Integer_info) 190 cf.constant_pool.get(ev.const_value_index); 191 isJdkExported = info.value != 0; 192 } 193 return Boolean.valueOf(isJdkExported); 194 } 195 } 196 } 197 return null; 198 } 199 200 void processJdkExported(ClassFile cf) throws IOException { 201 try { 202 String cn = cf.getName(); 203 String pn = cn.substring(0, cn.lastIndexOf('/')).replace('/', '.'); 204 205 Boolean b = isJdkExported(cf); 206 if (b != null) { 207 exportedTypes.put(cn.replace('/', '.'), b); 208 } 209 if (!exportedPackages.containsKey(pn)) { 210 // check if package-info.class has @jdk.Exported 211 Boolean isJdkExported = null; 212 ClassFile pcf = reader().getClassFile(cn.substring(0, cn.lastIndexOf('/')+1) + "package-info"); 213 if (pcf != null) { 214 isJdkExported = isJdkExported(pcf); 215 } 216 if (isJdkExported != null) { 217 exportedPackages.put(pn, isJdkExported); 218 } 219 } 220 } catch (ConstantPoolException e) { 221 throw new ClassFileError(e); 222 } 223 } 224 } 225 }