1 /* 2 * Copyright (c) 2006, 2016, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8003967 27 * @summary detect and remove all mutable implicit static enum fields in langtools 28 * @modules jdk.jdeps/com.sun.tools.classfile 29 * jdk.compiler/com.sun.tools.javac.util 30 * @run main DetectMutableStaticFields 31 */ 32 33 import java.io.File; 34 import java.io.IOException; 35 import java.net.URI; 36 import java.net.URISyntaxException; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.EnumSet; 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Set; 44 45 import javax.tools.JavaCompiler; 46 import javax.tools.JavaFileManager; 47 import javax.tools.JavaFileObject; 48 import javax.tools.StandardJavaFileManager; 49 import javax.tools.StandardLocation; 50 import javax.tools.ToolProvider; 51 52 import com.sun.tools.classfile.ClassFile; 53 import com.sun.tools.classfile.ConstantPoolException; 54 import com.sun.tools.classfile.Descriptor; 55 import com.sun.tools.classfile.Descriptor.InvalidDescriptor; 56 import com.sun.tools.classfile.Field; 57 58 import static javax.tools.JavaFileObject.Kind.CLASS; 59 60 import static com.sun.tools.classfile.AccessFlags.ACC_ENUM; 61 import static com.sun.tools.classfile.AccessFlags.ACC_FINAL; 62 import static com.sun.tools.classfile.AccessFlags.ACC_STATIC; 63 64 public class DetectMutableStaticFields { 65 66 private final String[] modules = { 67 "java.compiler", 68 "jdk.compiler", 69 "jdk.javadoc", 70 "jdk.jdeps" 71 }; 72 73 private final String[] packagesToSeekFor = new String[] { 74 "javax.tools", 75 "javax.lang.model", 76 "com.sun.javadoc", 77 "com.sun.source", 78 "com.sun.tools.classfile", 79 "com.sun.tools.doclets", 80 "com.sun.tools.javac", 81 "com.sun.tools.javadoc", 82 "com.sun.tools.javah", 83 "com.sun.tools.javap", 84 "jdk.javadoc" 85 }; 86 87 private static final Map<String, List<String>> classFieldsToIgnoreMap = new HashMap<>(); 88 private static void ignore(String className, String... fields) { 89 classFieldsToIgnoreMap.put(className, Arrays.asList(fields)); 90 } 91 92 static { 93 ignore("javax/tools/ToolProvider", "instance"); 94 ignore("com/sun/tools/javah/JavahTask", "versionRB"); 95 ignore("com/sun/tools/classfile/Dependencies$DefaultFilter", "instance"); 96 ignore("com/sun/tools/javap/JavapTask", "versionRB"); 97 ignore("com/sun/tools/doclets/formats/html/HtmlDoclet", "docletToStart"); 98 ignore("com/sun/tools/javac/util/JCDiagnostic", "fragmentFormatter"); 99 ignore("com/sun/tools/javac/util/JavacMessages", "defaultBundle", "defaultMessages"); 100 ignore("com/sun/tools/javac/file/JRTIndex", "sharedInstance"); 101 ignore("com/sun/tools/javac/main/JavaCompiler", "versionRB"); 102 ignore("com/sun/tools/javac/code/Type", "moreInfo"); 103 ignore("com/sun/tools/javac/util/SharedNameTable", "freelist"); 104 ignore("com/sun/tools/javac/util/Log", "useRawMessages"); 105 106 // The following static fields are used for caches of information obtained 107 // by reflective lookup, to avoid explicit references that are not available 108 // when running javac on JDK 8. 109 ignore("com/sun/tools/javac/util/ModuleHelper", 110 "addExportsMethod", "getModuleMethod", "getUnnamedModuleMethod"); 111 ignore("com/sun/tools/javac/util/JDK9Wrappers$Configuration", 112 "resolveRequiresAndUsesMethod", "configurationClass"); 113 ignore("com/sun/tools/javac/util/JDK9Wrappers$Layer", 114 "bootMethod", "defineModulesWithOneLoaderMethod", "configurationMethod", "layerClass"); 115 ignore("com/sun/tools/javac/util/JDK9Wrappers$ModuleFinder", 116 "moduleFinderClass", "ofMethod"); 117 ignore("com/sun/tools/javac/util/JDK9Wrappers$ServiceLoaderHelper", 118 "loadMethod"); 119 } 120 121 private final List<String> errors = new ArrayList<>(); 122 123 public static void main(String[] args) { 124 try { 125 new DetectMutableStaticFields().run(); 126 } catch (Exception ex) { 127 throw new AssertionError("Exception during test execution: " + ex, ex); 128 } 129 } 130 131 private void run() 132 throws 133 IOException, 134 ConstantPoolException, 135 InvalidDescriptor, 136 URISyntaxException { 137 138 JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 139 try (StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null)) { 140 for (String module: modules) { 141 analyzeModule(fm, module); 142 } 143 } 144 145 if (errors.size() > 0) { 146 for (String error: errors) { 147 System.err.println(error); 148 } 149 throw new AssertionError("There are mutable fields, " 150 + "please check output"); 151 } 152 } 153 154 boolean shouldAnalyzePackage(String packageName) { 155 for (String aPackage: packagesToSeekFor) { 156 if (packageName.contains(aPackage)) { 157 return true; 158 } 159 } 160 return false; 161 } 162 163 void analyzeModule(StandardJavaFileManager fm, String moduleName) 164 throws 165 IOException, 166 ConstantPoolException, 167 InvalidDescriptor { 168 JavaFileManager.Location location = 169 fm.getModuleLocation(StandardLocation.SYSTEM_MODULES, moduleName); 170 if (location == null) 171 throw new AssertionError("can't find module " + moduleName); 172 173 for (JavaFileObject file : fm.list(location, "", EnumSet.of(CLASS), true)) { 174 String className = fm.inferBinaryName(location, file); 175 int index = className.lastIndexOf('.'); 176 String pckName = index == -1 ? "" : className.substring(0, index); 177 if (shouldAnalyzePackage(pckName)) { 178 analyzeClassFile(ClassFile.read(file.openInputStream())); 179 } 180 } 181 } 182 183 List<String> currentFieldsToIgnore; 184 185 boolean ignoreField(String field) { 186 if (currentFieldsToIgnore != null) { 187 for (String fieldToIgnore : currentFieldsToIgnore) { 188 if (field.equals(fieldToIgnore)) { 189 return true; 190 } 191 } 192 } 193 return false; 194 } 195 196 void analyzeClassFile(ClassFile classFileToCheck) 197 throws 198 IOException, 199 ConstantPoolException, 200 Descriptor.InvalidDescriptor { 201 boolean enumClass = 202 (classFileToCheck.access_flags.flags & ACC_ENUM) != 0; 203 boolean nonFinalStaticEnumField; 204 boolean nonFinalStaticField; 205 206 currentFieldsToIgnore = 207 classFieldsToIgnoreMap.get(classFileToCheck.getName()); 208 209 for (Field field : classFileToCheck.fields) { 210 if (ignoreField(field.getName(classFileToCheck.constant_pool))) { 211 continue; 212 } 213 nonFinalStaticEnumField = 214 (field.access_flags.flags & (ACC_ENUM | ACC_FINAL)) == 0; 215 nonFinalStaticField = 216 (field.access_flags.flags & ACC_STATIC) != 0 && 217 (field.access_flags.flags & ACC_FINAL) == 0; 218 if (enumClass ? nonFinalStaticEnumField : nonFinalStaticField) { 219 errors.add("There is a mutable field named " + 220 field.getName(classFileToCheck.constant_pool) + 221 ", at class " + 222 classFileToCheck.getName()); 223 } 224 } 225 } 226 227 }