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/JDK9Wrappers$Configuration", 110 "resolveRequiresAndUsesMethod", "configurationClass"); 111 ignore("com/sun/tools/javac/util/JDK9Wrappers$Layer", 112 "bootMethod", "defineModulesWithOneLoaderMethod", "configurationMethod", "layerClass"); 113 ignore("com/sun/tools/javac/util/JDK9Wrappers$Module", 114 "addExportsMethod", "addUsesMethod", "getModuleMethod", "getUnnamedModuleMethod"); 115 ignore("com/sun/tools/javac/util/JDK9Wrappers$ModuleDescriptor$Version", 116 "versionClass", "parseMethod"); 117 ignore("com/sun/tools/javac/util/JDK9Wrappers$ModuleFinder", 118 "moduleFinderClass", "ofMethod"); 119 ignore("com/sun/tools/javac/util/JDK9Wrappers$ServiceLoaderHelper", 120 "loadMethod"); 121 ignore("com/sun/tools/javac/util/JDK9Wrappers$VMHelper", 122 "vmClass", "getRuntimeArgumentsMethod"); 123 ignore("com/sun/tools/javac/util/JDK9Wrappers$JmodFile", 124 "jmodFileClass", "checkMagicMethod"); 125 } 126 127 private final List<String> errors = new ArrayList<>(); 128 129 public static void main(String[] args) { 130 try { 131 new DetectMutableStaticFields().run(); 132 } catch (Exception ex) { 133 throw new AssertionError("Exception during test execution: " + ex, ex); 134 } 135 } 136 137 private void run() 138 throws 139 IOException, 140 ConstantPoolException, 141 InvalidDescriptor, 142 URISyntaxException { 143 144 JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 145 try (StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null)) { 146 for (String module: modules) { 147 analyzeModule(fm, module); 148 } 149 } 150 151 if (errors.size() > 0) { 152 for (String error: errors) { 153 System.err.println(error); 154 } 155 throw new AssertionError("There are mutable fields, " 156 + "please check output"); 157 } 158 } 159 160 boolean shouldAnalyzePackage(String packageName) { 161 for (String aPackage: packagesToSeekFor) { 162 if (packageName.contains(aPackage)) { 163 return true; 164 } 165 } 166 return false; 167 } 168 169 void analyzeModule(StandardJavaFileManager fm, String moduleName) 170 throws 171 IOException, 172 ConstantPoolException, 173 InvalidDescriptor { 174 JavaFileManager.Location location = 175 fm.getLocationForModule(StandardLocation.SYSTEM_MODULES, moduleName); 176 if (location == null) 177 throw new AssertionError("can't find module " + moduleName); 178 179 for (JavaFileObject file : fm.list(location, "", EnumSet.of(CLASS), true)) { 180 String className = fm.inferBinaryName(location, file); 181 int index = className.lastIndexOf('.'); 182 String pckName = index == -1 ? "" : className.substring(0, index); 183 if (shouldAnalyzePackage(pckName)) { 184 analyzeClassFile(ClassFile.read(file.openInputStream())); 185 } 186 } 187 } 188 189 List<String> currentFieldsToIgnore; 190 191 boolean ignoreField(String field) { 192 if (currentFieldsToIgnore != null) { 193 for (String fieldToIgnore : currentFieldsToIgnore) { 194 if (field.equals(fieldToIgnore)) { 195 return true; 196 } 197 } 198 } 199 return false; 200 } 201 202 void analyzeClassFile(ClassFile classFileToCheck) 203 throws 204 IOException, 205 ConstantPoolException, 206 Descriptor.InvalidDescriptor { 207 boolean enumClass = 208 (classFileToCheck.access_flags.flags & ACC_ENUM) != 0; 209 boolean nonFinalStaticEnumField; 210 boolean nonFinalStaticField; 211 212 currentFieldsToIgnore = 213 classFieldsToIgnoreMap.get(classFileToCheck.getName()); 214 215 for (Field field : classFileToCheck.fields) { 216 if (ignoreField(field.getName(classFileToCheck.constant_pool))) { 217 continue; 218 } 219 nonFinalStaticEnumField = 220 (field.access_flags.flags & (ACC_ENUM | ACC_FINAL)) == 0; 221 nonFinalStaticField = 222 (field.access_flags.flags & ACC_STATIC) != 0 && 223 (field.access_flags.flags & ACC_FINAL) == 0; 224 if (enumClass ? nonFinalStaticEnumField : nonFinalStaticField) { 225 errors.add("There is a mutable field named " + 226 field.getName(classFileToCheck.constant_pool) + 227 ", at class " + 228 classFileToCheck.getName()); 229 } 230 } 231 } 232 233 }