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", "jfxrt.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 public static boolean isProfileArchive(Archive archive) { 128 if (archive instanceof JDKArchive) { 129 return PROFILE_JARS.contains(archive.getName()); 130 } 131 return false; 132 } 133 134 private final Map<String,Boolean> exportedPackages = new HashMap<>(); 135 private final Map<String,Boolean> exportedTypes = new HashMap<>(); 136 JDKArchive(Path p) throws IOException { 137 super(p, ClassFileReader.newInstance(p)); 138 } 139 140 /** 141 * Tests if a given fully-qualified name is an exported type. 142 */ 143 public boolean isExported(String cn) { 144 int i = cn.lastIndexOf('.'); 145 String pn = i > 0 ? cn.substring(0, i) : ""; 146 147 boolean isJdkExported = isExportedPackage(pn); 148 if (exportedTypes.containsKey(cn)) { 149 return exportedTypes.get(cn); 150 } 151 return isJdkExported; 152 } 153 154 /** 155 * Tests if a given package name is exported. 156 */ 157 public boolean isExportedPackage(String pn) { 158 if (Profile.getProfile(pn) != null || "javax.jnlp".equals(pn)) { 159 return true; 160 } 161 return exportedPackages.containsKey(pn) ? exportedPackages.get(pn) : false; 162 } 163 164 private static final String JDK_EXPORTED_ANNOTATION = "Ljdk/Exported;"; 165 private Boolean isJdkExported(ClassFile cf) throws ConstantPoolException { 166 RuntimeAnnotations_attribute attr = (RuntimeAnnotations_attribute) 167 cf.attributes.get(RuntimeVisibleAnnotations); 168 if (attr != null) { 169 for (int i = 0; i < attr.annotations.length; i++) { 170 Annotation ann = attr.annotations[i]; 171 String annType = cf.constant_pool.getUTF8Value(ann.type_index); 172 if (JDK_EXPORTED_ANNOTATION.equals(annType)) { 173 boolean isJdkExported = true; 174 for (int j = 0; j < ann.num_element_value_pairs; j++) { 175 Annotation.element_value_pair pair = ann.element_value_pairs[j]; 176 Annotation.Primitive_element_value ev = (Annotation.Primitive_element_value) pair.value; 177 ConstantPool.CONSTANT_Integer_info info = (ConstantPool.CONSTANT_Integer_info) 178 cf.constant_pool.get(ev.const_value_index); 179 isJdkExported = info.value != 0; 180 } 181 return Boolean.valueOf(isJdkExported); 182 } 183 } 184 } 185 return null; 186 } 187 188 void processJdkExported(ClassFile cf) throws IOException { 189 try { 190 String cn = cf.getName(); 191 String pn = cn.substring(0, cn.lastIndexOf('/')).replace('/', '.'); 192 193 Boolean b = isJdkExported(cf); 194 if (b != null) { 195 exportedTypes.put(cn.replace('/', '.'), b); 196 } 197 if (!exportedPackages.containsKey(pn)) { 198 // check if package-info.class has @jdk.Exported 199 Boolean isJdkExported = null; 200 ClassFile pcf = reader().getClassFile(cn.substring(0, cn.lastIndexOf('/')+1) + "package-info"); 201 if (pcf != null) { 202 isJdkExported = isJdkExported(pcf); 203 } 204 if (isJdkExported != null) { 205 exportedPackages.put(pn, isJdkExported); 206 } 207 } 208 } catch (ConstantPoolException e) { 209 throw new ClassFileError(e); 210 } 211 } 212 } 213 }