aoqi@0: /* aoqi@0: * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: /* aoqi@0: * @test aoqi@0: * @bug 8003967 aoqi@0: * @summary detect and remove all mutable implicit static enum fields in langtools aoqi@0: * @run main DetectMutableStaticFields aoqi@0: */ aoqi@0: aoqi@0: import java.io.File; aoqi@0: import java.io.IOException; aoqi@0: import java.net.URI; aoqi@0: import java.net.URISyntaxException; aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.Arrays; aoqi@0: import java.util.EnumSet; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.List; aoqi@0: import java.util.Map; aoqi@0: import javax.tools.JavaCompiler; aoqi@0: import javax.tools.JavaFileManager; aoqi@0: import javax.tools.JavaFileObject; aoqi@0: import javax.tools.StandardJavaFileManager; aoqi@0: import javax.tools.StandardLocation; aoqi@0: import javax.tools.ToolProvider; aoqi@0: import com.sun.tools.classfile.ClassFile; aoqi@0: import com.sun.tools.classfile.ConstantPoolException; aoqi@0: import com.sun.tools.classfile.Descriptor; aoqi@0: import com.sun.tools.classfile.Descriptor.InvalidDescriptor; aoqi@0: import com.sun.tools.classfile.Field; aoqi@0: aoqi@0: import static javax.tools.JavaFileObject.Kind.CLASS; aoqi@0: import static com.sun.tools.classfile.AccessFlags.ACC_ENUM; aoqi@0: import static com.sun.tools.classfile.AccessFlags.ACC_FINAL; aoqi@0: import static com.sun.tools.classfile.AccessFlags.ACC_STATIC; aoqi@0: aoqi@0: public class DetectMutableStaticFields { aoqi@0: aoqi@0: private static final String keyResource = aoqi@0: "com/sun/tools/javac/tree/JCTree.class"; aoqi@0: aoqi@0: private String[] packagesToSeekFor = new String[] { aoqi@0: "javax.tools", aoqi@0: "javax.lang.model", aoqi@0: "com.sun.javadoc", aoqi@0: "com.sun.source", aoqi@0: "com.sun.tools.classfile", aoqi@0: "com.sun.tools.doclets", aoqi@0: "com.sun.tools.javac", aoqi@0: "com.sun.tools.javadoc", aoqi@0: "com.sun.tools.javah", aoqi@0: "com.sun.tools.javap", aoqi@0: }; aoqi@0: aoqi@0: private static final Map> classFieldsToIgnoreMap = new HashMap<>(); aoqi@0: aoqi@0: static { aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("javax/tools/ToolProvider", aoqi@0: Arrays.asList("instance")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/javah/JavahTask", aoqi@0: Arrays.asList("versionRB")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/classfile/Dependencies$DefaultFilter", aoqi@0: Arrays.asList("instance")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/javap/JavapTask", aoqi@0: Arrays.asList("versionRB")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/doclets/formats/html/HtmlDoclet", aoqi@0: Arrays.asList("docletToStart")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/javac/util/JCDiagnostic", aoqi@0: Arrays.asList("fragmentFormatter")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/javac/util/JavacMessages", aoqi@0: Arrays.asList("defaultBundle", "defaultMessages")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/javac/file/ZipFileIndexCache", aoqi@0: Arrays.asList("sharedInstance")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/javac/main/JavaCompiler", aoqi@0: Arrays.asList("versionRB")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/javac/code/Type", aoqi@0: Arrays.asList("moreInfo")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/javac/util/SharedNameTable", aoqi@0: Arrays.asList("freelist")); aoqi@0: classFieldsToIgnoreMap. aoqi@0: put("com/sun/tools/javac/util/Log", aoqi@0: Arrays.asList("useRawMessages")); aoqi@0: } aoqi@0: aoqi@0: private List errors = new ArrayList<>(); aoqi@0: aoqi@0: public static void main(String[] args) { aoqi@0: try { aoqi@0: new DetectMutableStaticFields().run(); aoqi@0: } catch (Exception ex) { aoqi@0: throw new AssertionError( aoqi@0: "Exception during test execution with cause ", aoqi@0: ex.getCause()); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private void run() aoqi@0: throws aoqi@0: IOException, aoqi@0: ConstantPoolException, aoqi@0: InvalidDescriptor, aoqi@0: URISyntaxException { aoqi@0: aoqi@0: URI resource = findResource(keyResource); aoqi@0: if (resource == null) { aoqi@0: throw new AssertionError("Resource " + keyResource + aoqi@0: "not found in the class path"); aoqi@0: } aoqi@0: analyzeResource(resource); aoqi@0: aoqi@0: if (errors.size() > 0) { aoqi@0: for (String error: errors) { aoqi@0: System.err.println(error); aoqi@0: } aoqi@0: throw new AssertionError("There are mutable fields, " aoqi@0: + "please check output"); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: URI findResource(String className) throws URISyntaxException { aoqi@0: URI uri = getClass().getClassLoader().getResource(className).toURI(); aoqi@0: if (uri.getScheme().equals("jar")) { aoqi@0: String ssp = uri.getRawSchemeSpecificPart(); aoqi@0: int sep = ssp.lastIndexOf("!"); aoqi@0: uri = new URI(ssp.substring(0, sep)); aoqi@0: } else if (uri.getScheme().equals("file")) { aoqi@0: uri = new URI(uri.getPath().substring(0, aoqi@0: uri.getPath().length() - keyResource.length())); aoqi@0: } aoqi@0: return uri; aoqi@0: } aoqi@0: aoqi@0: boolean shouldAnalyzePackage(String packageName) { aoqi@0: for (String aPackage: packagesToSeekFor) { aoqi@0: if (packageName.contains(aPackage)) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: void analyzeResource(URI resource) aoqi@0: throws aoqi@0: IOException, aoqi@0: ConstantPoolException, aoqi@0: InvalidDescriptor { aoqi@0: JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); aoqi@0: StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null); aoqi@0: JavaFileManager.Location location = aoqi@0: StandardLocation.locationFor(resource.getPath()); aoqi@0: fm.setLocation(location, com.sun.tools.javac.util.List.of( aoqi@0: new File(resource.getPath()))); aoqi@0: aoqi@0: for (JavaFileObject file : fm.list(location, "", EnumSet.of(CLASS), true)) { aoqi@0: String className = fm.inferBinaryName(location, file); aoqi@0: int index = className.lastIndexOf('.'); aoqi@0: String pckName = index == -1 ? "" : className.substring(0, index); aoqi@0: if (shouldAnalyzePackage(pckName)) { aoqi@0: analyzeClassFile(ClassFile.read(file.openInputStream())); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: List currentFieldsToIgnore; aoqi@0: aoqi@0: boolean ignoreField(String field) { aoqi@0: if (currentFieldsToIgnore != null) { aoqi@0: for (String fieldToIgnore : currentFieldsToIgnore) { aoqi@0: if (field.equals(fieldToIgnore)) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: void analyzeClassFile(ClassFile classFileToCheck) aoqi@0: throws aoqi@0: IOException, aoqi@0: ConstantPoolException, aoqi@0: Descriptor.InvalidDescriptor { aoqi@0: boolean enumClass = aoqi@0: (classFileToCheck.access_flags.flags & ACC_ENUM) != 0; aoqi@0: boolean nonFinalStaticEnumField; aoqi@0: boolean nonFinalStaticField; aoqi@0: aoqi@0: currentFieldsToIgnore = aoqi@0: classFieldsToIgnoreMap.get(classFileToCheck.getName()); aoqi@0: aoqi@0: for (Field field : classFileToCheck.fields) { aoqi@0: if (ignoreField(field.getName(classFileToCheck.constant_pool))) { aoqi@0: continue; aoqi@0: } aoqi@0: nonFinalStaticEnumField = aoqi@0: (field.access_flags.flags & (ACC_ENUM | ACC_FINAL)) == 0; aoqi@0: nonFinalStaticField = aoqi@0: (field.access_flags.flags & ACC_STATIC) != 0 && aoqi@0: (field.access_flags.flags & ACC_FINAL) == 0; aoqi@0: if (enumClass ? nonFinalStaticEnumField : nonFinalStaticField) { aoqi@0: errors.add("There is a mutable field named " + aoqi@0: field.getName(classFileToCheck.constant_pool) + aoqi@0: ", at class " + aoqi@0: classFileToCheck.getName()); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: }