DetectMutableStaticFields.java revision 2942:08092deced3f
1/*
2 * Copyright (c) 2006, 2015, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * @test
28 * @bug 8003967
29 * @summary detect and remove all mutable implicit static enum fields in langtools
30 * @modules jdk.jdeps/com.sun.tools.classfile
31 *          jdk.compiler/com.sun.tools.javac.util
32 * @run main DetectMutableStaticFields
33 */
34
35import java.io.File;
36import java.io.IOException;
37import java.net.URI;
38import java.net.URISyntaxException;
39import java.util.ArrayList;
40import java.util.Arrays;
41import java.util.EnumSet;
42import java.util.HashMap;
43import java.util.List;
44import java.util.Map;
45import javax.tools.JavaCompiler;
46import javax.tools.JavaFileManager;
47import javax.tools.JavaFileObject;
48import javax.tools.StandardJavaFileManager;
49import javax.tools.StandardLocation;
50import javax.tools.ToolProvider;
51import com.sun.tools.classfile.ClassFile;
52import com.sun.tools.classfile.ConstantPoolException;
53import com.sun.tools.classfile.Descriptor;
54import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
55import com.sun.tools.classfile.Field;
56
57import static javax.tools.JavaFileObject.Kind.CLASS;
58import static com.sun.tools.classfile.AccessFlags.ACC_ENUM;
59import static com.sun.tools.classfile.AccessFlags.ACC_FINAL;
60import static com.sun.tools.classfile.AccessFlags.ACC_STATIC;
61
62public class DetectMutableStaticFields {
63
64    private static final String keyResource =
65            "com/sun/tools/javac/tree/JCTree.class";
66
67    private String[] packagesToSeekFor = new String[] {
68        "javax.tools",
69        "javax.lang.model",
70        "com.sun.javadoc",
71        "com.sun.source",
72        "com.sun.tools.classfile",
73        "com.sun.tools.doclets",
74        "com.sun.tools.javac",
75        "com.sun.tools.javadoc",
76        "com.sun.tools.javah",
77        "com.sun.tools.javap",
78    };
79
80    private static final Map<String, List<String>> classFieldsToIgnoreMap = new HashMap<>();
81
82    static {
83        classFieldsToIgnoreMap.
84                put("javax/tools/ToolProvider",
85                    Arrays.asList("instance"));
86        classFieldsToIgnoreMap.
87                put("com/sun/tools/javah/JavahTask",
88                    Arrays.asList("versionRB"));
89        classFieldsToIgnoreMap.
90                put("com/sun/tools/classfile/Dependencies$DefaultFilter",
91                    Arrays.asList("instance"));
92        classFieldsToIgnoreMap.
93                put("com/sun/tools/javap/JavapTask",
94                    Arrays.asList("versionRB"));
95        classFieldsToIgnoreMap.
96                put("com/sun/tools/doclets/formats/html/HtmlDoclet",
97                    Arrays.asList("docletToStart"));
98        classFieldsToIgnoreMap.
99                put("com/sun/tools/javac/util/JCDiagnostic",
100                    Arrays.asList("fragmentFormatter"));
101        classFieldsToIgnoreMap.
102                put("com/sun/tools/javac/util/JavacMessages",
103                    Arrays.asList("defaultBundle", "defaultMessages"));
104        classFieldsToIgnoreMap.
105                put("com/sun/tools/javac/file/ZipFileIndexCache",
106                    Arrays.asList("sharedInstance"));
107        classFieldsToIgnoreMap.
108                put("com/sun/tools/javac/file/JRTIndex",
109                    Arrays.asList("sharedInstance"));
110        classFieldsToIgnoreMap.
111                put("com/sun/tools/javac/main/JavaCompiler",
112                    Arrays.asList("versionRB"));
113        classFieldsToIgnoreMap.
114                put("com/sun/tools/javac/code/Type",
115                    Arrays.asList("moreInfo"));
116        classFieldsToIgnoreMap.
117                put("com/sun/tools/javac/util/SharedNameTable",
118                    Arrays.asList("freelist"));
119        classFieldsToIgnoreMap.
120                put("com/sun/tools/javac/util/Log",
121                    Arrays.asList("useRawMessages"));
122    }
123
124    private List<String> errors = new ArrayList<>();
125
126    public static void main(String[] args) {
127        try {
128            new DetectMutableStaticFields().run();
129        } catch (Exception ex) {
130            throw new AssertionError(
131                    "Exception during test execution with cause ",
132                    ex.getCause());
133        }
134    }
135
136    private void run()
137        throws
138            IOException,
139            ConstantPoolException,
140            InvalidDescriptor,
141            URISyntaxException {
142
143        URI resource = findResource(keyResource);
144        if (resource == null) {
145            throw new AssertionError("Resource " + keyResource +
146                "not found in the class path");
147        }
148        analyzeResource(resource);
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    URI findResource(String className) throws URISyntaxException {
160        URI uri = getClass().getClassLoader().getResource(className).toURI();
161        if (uri.getScheme().equals("jar")) {
162            String ssp = uri.getRawSchemeSpecificPart();
163            int sep = ssp.lastIndexOf("!");
164            uri = new URI(ssp.substring(0, sep));
165        } else if (uri.getScheme().equals("file")) {
166            uri = new URI(uri.getPath().substring(0,
167                    uri.getPath().length() - keyResource.length()));
168        }
169        return uri;
170    }
171
172    boolean shouldAnalyzePackage(String packageName) {
173        for (String aPackage: packagesToSeekFor) {
174            if (packageName.contains(aPackage)) {
175                return true;
176            }
177        }
178        return false;
179    }
180
181    void analyzeResource(URI resource)
182        throws
183            IOException,
184            ConstantPoolException,
185            InvalidDescriptor {
186        JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
187        try (StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null)) {
188            JavaFileManager.Location location =
189                    StandardLocation.locationFor(resource.getPath());
190            fm.setLocation(location, com.sun.tools.javac.util.List.of(
191                    new File(resource.getPath())));
192
193            for (JavaFileObject file : fm.list(location, "", EnumSet.of(CLASS), true)) {
194                String className = fm.inferBinaryName(location, file);
195                int index = className.lastIndexOf('.');
196                String pckName = index == -1 ? "" : className.substring(0, index);
197                if (shouldAnalyzePackage(pckName)) {
198                    analyzeClassFile(ClassFile.read(file.openInputStream()));
199                }
200            }
201        }
202    }
203
204    List<String> currentFieldsToIgnore;
205
206    boolean ignoreField(String field) {
207        if (currentFieldsToIgnore != null) {
208            for (String fieldToIgnore : currentFieldsToIgnore) {
209                if (field.equals(fieldToIgnore)) {
210                    return true;
211                }
212            }
213        }
214        return false;
215    }
216
217    void analyzeClassFile(ClassFile classFileToCheck)
218        throws
219            IOException,
220            ConstantPoolException,
221            Descriptor.InvalidDescriptor {
222        boolean enumClass =
223                (classFileToCheck.access_flags.flags & ACC_ENUM) != 0;
224        boolean nonFinalStaticEnumField;
225        boolean nonFinalStaticField;
226
227        currentFieldsToIgnore =
228                classFieldsToIgnoreMap.get(classFileToCheck.getName());
229
230        for (Field field : classFileToCheck.fields) {
231            if (ignoreField(field.getName(classFileToCheck.constant_pool))) {
232                continue;
233            }
234            nonFinalStaticEnumField =
235                    (field.access_flags.flags & (ACC_ENUM | ACC_FINAL)) == 0;
236            nonFinalStaticField =
237                    (field.access_flags.flags & ACC_STATIC) != 0 &&
238                    (field.access_flags.flags & ACC_FINAL) == 0;
239            if (enumClass ? nonFinalStaticEnumField : nonFinalStaticField) {
240                errors.add("There is a mutable field named " +
241                        field.getName(classFileToCheck.constant_pool) +
242                        ", at class " +
243                        classFileToCheck.getName());
244            }
245        }
246    }
247
248}
249