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