1 /* 2 * Copyright (c) 2006, 2017, 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.javap", 83 "jdk.javadoc" 84 }; 85 86 private static final Map<String, List<String>> classFieldsToIgnoreMap = new HashMap<>(); 87 private static void ignore(String className, String... fields) { 88 classFieldsToIgnoreMap.put(className, Arrays.asList(fields)); 89 } 90 91 static { 92 ignore("javax/tools/ToolProvider", "instance"); 93 ignore("jdk/javadoc/internal/tool/Start", "versionRB"); 94 ignore("com/sun/tools/classfile/Dependencies$DefaultFilter", "instance"); 95 ignore("com/sun/tools/javap/JavapTask", "versionRB"); 96 ignore("com/sun/tools/doclets/formats/html/HtmlDoclet", "docletToStart"); 97 ignore("com/sun/tools/javac/util/JCDiagnostic", "fragmentFormatter"); 98 ignore("com/sun/tools/javac/util/JavacMessages", "defaultBundle", "defaultMessages"); 99 ignore("com/sun/tools/javac/file/JRTIndex", "sharedInstance"); 100 ignore("com/sun/tools/javac/main/JavaCompiler", "versionRB"); 101 ignore("com/sun/tools/javac/code/Type", "moreInfo"); 102 ignore("com/sun/tools/javac/util/SharedNameTable", "freelist"); 103 ignore("com/sun/tools/javac/util/Log", "useRawMessages"); 104 105 // The following static fields are used for caches of information obtained 106 // by reflective lookup, to avoid explicit references that are not available 107 // when running javac on JDK 8. 108 ignore("com/sun/tools/javac/util/JDK9Wrappers$Configuration", 109 "resolveAndBindMethod", "configurationClass"); 110 ignore("com/sun/tools/javac/util/JDK9Wrappers$Layer", 111 "bootMethod", "defineModulesWithOneLoaderMethod", "configurationMethod", "layerClass"); 112 ignore("com/sun/tools/javac/util/JDK9Wrappers$Module", 113 "addExportsMethod", "addUsesMethod", "getModuleMethod", "getUnnamedModuleMethod"); 114 ignore("com/sun/tools/javac/util/JDK9Wrappers$ModuleDescriptor$Version", 115 "versionClass", "parseMethod"); 116 ignore("com/sun/tools/javac/util/JDK9Wrappers$ModuleFinder", 117 "moduleFinderClass", "ofMethod"); 118 ignore("com/sun/tools/javac/util/JDK9Wrappers$ServiceLoaderHelper", 119 "loadMethod"); 120 ignore("com/sun/tools/javac/util/JDK9Wrappers$VMHelper", 121 "vmClass", "getRuntimeArgumentsMethod"); 122 ignore("com/sun/tools/javac/util/JDK9Wrappers$JmodFile", 123 "jmodFileClass", "checkMagicMethod"); 124 } 125 126 private final List<String> errors = new ArrayList<>(); 127 128 public static void main(String[] args) { 129 try { 130 new DetectMutableStaticFields().run(); 131 } catch (Exception ex) { 132 throw new AssertionError("Exception during test execution: " + ex, ex); 133 } 134 } 135 136 private void run() 137 throws 138 IOException, 139 ConstantPoolException, 140 InvalidDescriptor, 141 URISyntaxException { 142 143 JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 144 try (StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null)) { 145 for (String module: modules) { 146 analyzeModule(fm, module); 147 } 148 } 149 150 if (errors.size() > 0) { 151 for (String error: errors) { 152 System.err.println(error); 153 } 154 throw new AssertionError("There are mutable fields, " 155 + "please check output"); 156 } 157 } 158 159 boolean shouldAnalyzePackage(String packageName) { 160 for (String aPackage: packagesToSeekFor) { 161 if (packageName.contains(aPackage)) { 162 return true; 163 } 164 } 165 return false; 166 } 167 168 void analyzeModule(StandardJavaFileManager fm, String moduleName) 169 throws 170 IOException, 171 ConstantPoolException, 172 InvalidDescriptor { 173 JavaFileManager.Location location = 174 fm.getLocationForModule(StandardLocation.SYSTEM_MODULES, moduleName); 175 if (location == null) 176 throw new AssertionError("can't find module " + moduleName); 177 178 for (JavaFileObject file : fm.list(location, "", EnumSet.of(CLASS), true)) { 179 String className = fm.inferBinaryName(location, file); 180 int index = className.lastIndexOf('.'); 181 String pckName = index == -1 ? "" : className.substring(0, index); 182 if (shouldAnalyzePackage(pckName)) { 183 analyzeClassFile(ClassFile.read(file.openInputStream())); 184 } 185 } 186 } 187 188 List<String> currentFieldsToIgnore; 189 190 boolean ignoreField(String field) { 191 if (currentFieldsToIgnore != null) { 192 for (String fieldToIgnore : currentFieldsToIgnore) { 193 if (field.equals(fieldToIgnore)) { 194 return true; 195 } 196 } 197 } 198 return false; 199 } 200 201 void analyzeClassFile(ClassFile classFileToCheck) 202 throws 203 IOException, 204 ConstantPoolException, 205 Descriptor.InvalidDescriptor { 206 boolean enumClass = 207 (classFileToCheck.access_flags.flags & ACC_ENUM) != 0; 208 boolean nonFinalStaticEnumField; 209 boolean nonFinalStaticField; 210 211 currentFieldsToIgnore = 212 classFieldsToIgnoreMap.get(classFileToCheck.getName()); 213 214 for (Field field : classFileToCheck.fields) { 215 if (ignoreField(field.getName(classFileToCheck.constant_pool))) { 216 continue; 217 } 218 nonFinalStaticEnumField = 219 (field.access_flags.flags & (ACC_ENUM | ACC_FINAL)) == 0; 220 nonFinalStaticField = 221 (field.access_flags.flags & ACC_STATIC) != 0 && 222 (field.access_flags.flags & ACC_FINAL) == 0; 223 if (enumClass ? nonFinalStaticEnumField : nonFinalStaticField) { 224 errors.add("There is a mutable field named " + 225 field.getName(classFileToCheck.constant_pool) + 226 ", at class " + 227 classFileToCheck.getName()); 228 } 229 } 230 } 231 232 }