diff --git a/make/ToolsLangtools.gmk b/make/ToolsLangtools.gmk index 4146652bf8b07..1a764d6019be0 100644 --- a/make/ToolsLangtools.gmk +++ b/make/ToolsLangtools.gmk @@ -36,7 +36,7 @@ $(eval $(call SetupJavaCompilation, BUILD_TOOLS_LANGTOOLS, \ COMPILER := bootjdk, \ TARGET_RELEASE := $(TARGET_RELEASE_BOOTJDK), \ SRC := $(TOPDIR)/make/langtools/tools, \ - INCLUDES := compileproperties propertiesparser, \ + INCLUDES := compileproperties flagsgenerator propertiesparser, \ COPY := .properties, \ BIN := $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes, \ )) diff --git a/make/langtools/tools/flagsgenerator/FlagsGenerator.java b/make/langtools/tools/flagsgenerator/FlagsGenerator.java new file mode 100644 index 0000000000000..1c192d214dee8 --- /dev/null +++ b/make/langtools/tools/flagsgenerator/FlagsGenerator.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package flagsgenerator; + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.util.JavacTask; +import com.sun.source.util.TreePath; +import com.sun.source.util.Trees; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.util.ElementFilter; +import javax.tools.ToolProvider; + +public class FlagsGenerator { + public static void main(String... args) throws IOException { + var compiler = ToolProvider.getSystemJavaCompiler(); + + try (var fm = compiler.getStandardFileManager(null, null, null)) { + JavacTask task = (JavacTask) compiler.getTask(null, null, d -> {}, null, null, fm.getJavaFileObjects(args[0])); + Trees trees = Trees.instance(task); + CompilationUnitTree cut = task.parse().iterator().next(); + + task.analyze(); + + TypeElement clazz = (TypeElement) trees.getElement(new TreePath(new TreePath(cut), cut.getTypeDecls().get(0))); + Map> flag2Names = new TreeMap<>(); + Map>> target2FlagBit2Fields = new EnumMap<>(FlagTarget.class); + Map customToString = new HashMap<>(); + Set noToString = new HashSet<>(); + + for (VariableElement field : ElementFilter.fieldsIn(clazz.getEnclosedElements())) { + String flagName = field.getSimpleName().toString(); + for (AnnotationMirror am : field.getAnnotationMirrors()) { + switch (am.getAnnotationType().toString()) { + case "com.sun.tools.javac.code.Flags.Use" -> { + long flagValue = ((Number) field.getConstantValue()).longValue(); + int flagBit = 63 - Long.numberOfLeadingZeros(flagValue); + + flag2Names.computeIfAbsent(flagBit, _ -> new ArrayList<>()) + .add(flagName); + + List originalTargets = (List) valueOfValueAttribute(am); + originalTargets.stream() + .map(value -> FlagTarget.valueOf(value.toString())) + .forEach(target -> target2FlagBit2Fields.computeIfAbsent(target, _ -> new HashMap<>()) + .computeIfAbsent(flagBit, _ -> new ArrayList<>()) + .add(flagName)); + } + case "com.sun.tools.javac.code.Flags.CustomToStringValue" -> { + customToString.put(flagName, (String) valueOfValueAttribute(am)); + } + case "com.sun.tools.javac.code.Flags.NoToStringValue" -> { + noToString.add(flagName); + } + } + } + } + + //verify there are no flag overlaps: + for (Entry>> targetAndFlag : target2FlagBit2Fields.entrySet()) { + for (Entry> flagAndFields : targetAndFlag.getValue().entrySet()) { + if (flagAndFields.getValue().size() > 1) { + throw new AssertionError("duplicate flag for target: " + targetAndFlag.getKey() + + ", flag: " + flagAndFields.getKey() + + ", flags fields: " + flagAndFields.getValue()); + } + } + } + + try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(Paths.get(args[1])))) { + out.println(""" + package com.sun.tools.javac.code; + + public enum FlagsEnum { + """); + for (Entry> e : flag2Names.entrySet()) { + String constantName = e.getValue().stream().collect(Collectors.joining("_OR_")); + String toString = e.getValue() + .stream() + .filter(n -> !noToString.contains(n)) + .map(n -> customToString.getOrDefault(n, n.toLowerCase(Locale.US))) + .collect(Collectors.joining(" or ")); + out.println(" " + constantName + "(1L<<" + e.getKey() + ", \"" + toString + "\"),"); + } + out.println(""" + ; + + private final long value; + private final String toString; + private FlagsEnum(long value, String toString) { + this.value = value; + this.toString = toString; + } + public long value() { + return value; + } + public String toString() { + return toString; + } + } + """); + } + } + } + + private static Object valueOfValueAttribute(AnnotationMirror am) { + return am.getElementValues() + .values() + .iterator() + .next() + .getValue(); + } + + private enum FlagTarget { + BLOCK, + CLASS, + METHOD, + MODULE, + PACKAGE, + TYPE_VAR, + VARIABLE; + } +} diff --git a/make/langtools/tools/propertiesparser/parser/MessageType.java b/make/langtools/tools/propertiesparser/parser/MessageType.java index ea518dc536bf8..a4ea0ddc3c011 100644 --- a/make/langtools/tools/propertiesparser/parser/MessageType.java +++ b/make/langtools/tools/propertiesparser/parser/MessageType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,7 +76,7 @@ public enum SimpleType implements MessageType { ANNOTATION("annotation", "Compound", "com.sun.tools.javac.code.Attribute"), BOOLEAN("boolean", "boolean", null), COLLECTION("collection", "Collection", "java.util"), - FLAG("flag", "Flag", "com.sun.tools.javac.code.Flags"), + FLAG("flag", "FlagsEnum", "com.sun.tools.javac.code"), FRAGMENT("fragment", "Fragment", null), DIAGNOSTIC("diagnostic", "JCDiagnostic", "com.sun.tools.javac.util"), MODIFIER("modifier", "Modifier", "javax.lang.model.element"), diff --git a/make/modules/jdk.compiler/Gensrc.gmk b/make/modules/jdk.compiler/Gensrc.gmk index c6c5879745cf4..501ea37ae8b4d 100644 --- a/make/modules/jdk.compiler/Gensrc.gmk +++ b/make/modules/jdk.compiler/Gensrc.gmk @@ -41,17 +41,17 @@ $(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ TARGETS += $(COMPILE_PROPERTIES) -################################################################################ -# -# Compile properties files into enum-like classes using the propertiesparser tool -# - # To avoid reevaluating the compilation setup for the tools each time this file # is included, the following trick is used to be able to declare a dependency on # the built tools. BUILD_TOOLS_LANGTOOLS := $(call SetupJavaCompilationCompileTarget, \ BUILD_TOOLS_LANGTOOLS, $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes) +################################################################################ +# +# Compile properties files into enum-like classes using the propertiesparser tool +# + TOOL_PARSEPROPERTIES_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \ propertiesparser.PropertiesParser @@ -76,3 +76,26 @@ $(eval $(call SetupExecute, PARSEPROPERTIES, \ TARGETS += $(PARSEPROPERTIES) ################################################################################ +# +# Generate FlagsEnum from Flags constants +# + +TOOL_FLAGSGENERATOR_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \ + flagsgenerator.FlagsGenerator + +FLAGS_SRC := \ + $(MODULE_SRC)/share/classes/com/sun/tools/javac/code/Flags.java + +FLAGS_OUT := \ + $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/com/sun/tools/javac/code/FlagsEnum.java + +$(eval $(call SetupExecute, FLAGSGENERATOR, \ + WARN := Generating FlagsEnum, \ + DEPS := $(FLAGS_SRC) $(BUILD_TOOLS_LANGTOOLS), \ + OUTPUT_FILE := $(FLAGS_OUT), \ + COMMAND := $(TOOL_FLAGSGENERATOR_CMD) $(FLAGS_SRC) $(FLAGS_OUT), \ +)) + +TARGETS += $(FLAGSGENERATOR) + +################################################################################ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index 26f0d338a2eed..5b59e47027e59 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -25,6 +25,8 @@ package com.sun.tools.javac.code; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.EnumSet; import java.util.Map; @@ -35,7 +37,6 @@ import javax.lang.model.element.Modifier; import com.sun.tools.javac.util.Assert; -import com.sun.tools.javac.util.StringUtils; /** Access flags and other modifiers for Java classes and members. * @@ -51,7 +52,7 @@ private Flags() {} // uninstantiable public static String toString(long flags) { StringBuilder buf = new StringBuilder(); String sep = ""; - for (Flag flag : asFlagSet(flags)) { + for (FlagsEnum flag : asFlagSet(flags)) { buf.append(sep); buf.append(flag); sep = " "; @@ -59,12 +60,12 @@ public static String toString(long flags) { return buf.toString(); } - public static EnumSet asFlagSet(long flags) { - EnumSet flagSet = EnumSet.noneOf(Flag.class); - for (Flag flag : Flag.values()) { - if ((flags & flag.value) != 0) { + public static EnumSet asFlagSet(long flags) { + EnumSet flagSet = EnumSet.noneOf(FlagsEnum.class); + for (FlagsEnum flag : FlagsEnum.values()) { + if ((flags & flag.value()) != 0) { flagSet.add(flag); - flags &= ~flag.value; + flags &= ~flag.value(); } } Assert.check(flags == 0); @@ -73,42 +74,67 @@ public static EnumSet asFlagSet(long flags) { /* Standard Java flags. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PUBLIC = 1; + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PRIVATE = 1<<1; + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PROTECTED = 1<<2; + @Use({FlagTarget.BLOCK, FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int STATIC = 1<<3; + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int FINAL = 1<<4; + @Use({FlagTarget.METHOD}) public static final int SYNCHRONIZED = 1<<5; + @Use({FlagTarget.VARIABLE}) public static final int VOLATILE = 1<<6; + @Use({FlagTarget.VARIABLE}) public static final int TRANSIENT = 1<<7; + @Use({FlagTarget.METHOD}) public static final int NATIVE = 1<<8; + @Use({FlagTarget.CLASS}) public static final int INTERFACE = 1<<9; + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final int ABSTRACT = 1<<10; + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final int STRICTFP = 1<<11; /* Flag that marks a symbol synthetic, added in classfile v49.0. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int SYNTHETIC = 1<<12; /** Flag that marks attribute interfaces, added in classfile v49.0. */ + @Use({FlagTarget.CLASS}) public static final int ANNOTATION = 1<<13; /** An enumeration type or an enumeration constant, added in * classfile v49.0. */ + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE}) public static final int ENUM = 1<<14; /** Added in SE8, represents constructs implicitly declared in source. */ + @Use({FlagTarget.MODULE, FlagTarget.VARIABLE}) public static final int MANDATED = 1<<15; + @NotFlag public static final int StandardFlags = 0x0fff; // Because the following access flags are overloaded with other // bit positions, we translate them when reading and writing class // files into unique bits positions: ACC_SYNTHETIC <-> SYNTHETIC, // for example. - public static final int ACC_SUPER = 0x0020; - public static final int ACC_BRIDGE = 0x0040; - public static final int ACC_VARARGS = 0x0080; - public static final int ACC_MODULE = 0x8000; + @Use({FlagTarget.CLASS}) + @NoToStringValue + public static final int ACC_SUPER = 1<<5; + @Use({FlagTarget.METHOD}) + @NoToStringValue + public static final int ACC_BRIDGE = 1<<6; + @Use({FlagTarget.METHOD}) + @NoToStringValue + public static final int ACC_VARARGS = 1<<7; + @Use({FlagTarget.CLASS}) + @NoToStringValue + public static final int ACC_MODULE = 1<<15; /* *************************************** * Internal compiler flags (no bits in the lower 16). @@ -116,25 +142,30 @@ public static EnumSet asFlagSet(long flags) { /** Flag is set if symbol is deprecated. See also DEPRECATED_REMOVAL. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final int DEPRECATED = 1<<17; /** Flag is set for a variable symbol if the variable's definition * has an initializer part. */ + @Use({FlagTarget.VARIABLE}) public static final int HASINIT = 1<<18; /** Class is an implicitly declared top level class. */ + @Use({FlagTarget.CLASS}) public static final int IMPLICIT_CLASS = 1<<19; /** Flag is set for compiler-generated anonymous method symbols * that `own' an initializer block. */ + @Use({FlagTarget.METHOD}) public static final int BLOCK = 1<<20; /** Flag is set for ClassSymbols that are being compiled from source. */ - public static final int FROM_SOURCE = 1<<21; //ClassSymbols + @Use({FlagTarget.CLASS}) + public static final int FROM_SOURCE = 1<<21; /** Flag is set for nested classes that do not access instance members * or `this' of an outer class and therefore don't need to be passed @@ -143,25 +174,30 @@ public static EnumSet asFlagSet(long flags) { * todo: use this value for optimizing away this$n parameters in * other cases. */ + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE}) public static final int NOOUTERTHIS = 1<<22; /** Flag is set for package symbols if a package has a member or * directory and therefore exists. */ + @Use({FlagTarget.CLASS, FlagTarget.PACKAGE}) public static final int EXISTS = 1<<23; /** Flag is set for compiler-generated compound classes * representing multiple variable bounds */ + @Use({FlagTarget.CLASS}) public static final int COMPOUND = 1<<24; /** Flag is set for class symbols if a class file was found for this class. */ + @Use({FlagTarget.CLASS}) public static final int CLASS_SEEN = 1<<25; /** Flag is set for class symbols if a source file was found for this * class. */ + @Use({FlagTarget.CLASS}) public static final int SOURCE_SEEN = 1<<26; /* State flags (are reset during compilation). @@ -172,242 +208,291 @@ public static EnumSet asFlagSet(long flags) { * relations. Similarly for constructor call cycle detection in * Attr. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final int LOCKED = 1<<27; /** Flag for class symbols is set and later re-set to indicate that a class * has been entered but has not yet been attributed. */ + @Use({FlagTarget.CLASS}) public static final int UNATTRIBUTED = 1<<28; /** Flag for synthesized default constructors of anonymous classes. */ - public static final int ANONCONSTR = 1<<29; //non-class members + @Use({FlagTarget.METHOD}) + public static final int ANONCONSTR = 1<<29; /** * Flag to indicate the superclasses of this ClassSymbol has been attributed. */ - public static final int SUPER_OWNER_ATTRIBUTED = 1<<29; //ClassSymbols + @Use({FlagTarget.CLASS}) + public static final int SUPER_OWNER_ATTRIBUTED = 1<<29; /** Flag for class symbols to indicate it has been checked and found * acyclic. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.TYPE_VAR}) public static final int ACYCLIC = 1<<30; /** Flag that marks bridge methods. */ + @Use({FlagTarget.METHOD}) public static final long BRIDGE = 1L<<31; /** Flag that marks formal parameters. */ + @Use({FlagTarget.VARIABLE}) public static final long PARAMETER = 1L<<33; /** Flag that marks varargs methods. */ + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final long VARARGS = 1L<<34; /** Flag for annotation type symbols to indicate it has been * checked and found acyclic. */ + @Use({FlagTarget.CLASS}) public static final long ACYCLIC_ANN = 1L<<35; /** Flag that marks a generated default constructor. */ + @Use({FlagTarget.METHOD}) public static final long GENERATEDCONSTR = 1L<<36; /** Flag that marks a hypothetical method that need not really be * generated in the binary, but is present in the symbol table to * simplify checking for erasure clashes - also used for 292 poly sig methods. */ + @Use({FlagTarget.METHOD}) public static final long HYPOTHETICAL = 1L<<37; /** * Flag that marks an internal proprietary class. */ + @Use({FlagTarget.CLASS}) public static final long PROPRIETARY = 1L<<38; /** * Flag that marks a multi-catch parameter. */ + @Use({FlagTarget.VARIABLE}) public static final long UNION = 1L<<39; /** * Flags an erroneous TypeSymbol as viable for recovery. * TypeSymbols only. */ + @Use({FlagTarget.CLASS, FlagTarget.TYPE_VAR}) public static final long RECOVERABLE = 1L<<40; /** * Flag that marks an 'effectively final' local variable. */ + @Use({FlagTarget.VARIABLE}) public static final long EFFECTIVELY_FINAL = 1L<<41; /** * Flag that marks non-override equivalent methods with the same signature, * or a conflicting match binding (BindingSymbol). */ + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final long CLASH = 1L<<42; /** * Flag that marks either a default method or an interface containing default methods. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final long DEFAULT = 1L<<43; // part of ExtendedStandardFlags, cannot be reused /** * Flag that marks class as auxiliary, ie a non-public class following * the public class in a source file, that could block implicit compilation. */ + @Use({FlagTarget.CLASS}) public static final long AUXILIARY = 1L<<44; /** * Flag that marks that a symbol is not available in the current profile */ + @Use({FlagTarget.CLASS}) public static final long NOT_IN_PROFILE = 1L<<45; /** * Flag that indicates that an override error has been detected by Check. */ + @Use({FlagTarget.METHOD}) public static final long BAD_OVERRIDE = 1L<<45; /** * Flag that indicates a signature polymorphic method (292). */ + @Use({FlagTarget.METHOD}) public static final long SIGNATURE_POLYMORPHIC = 1L<<46; /** * Flag that indicates that an inference variable is used in a 'throws' clause. */ + @Use({FlagTarget.TYPE_VAR}) public static final long THROWS = 1L<<47; /** * Flag to indicate sealed class/interface declaration. */ + @Use({FlagTarget.CLASS}) public static final long SEALED = 1L<<48; // part of ExtendedStandardFlags, cannot be reused /** * Flag that marks a synthetic method body for a lambda expression */ - public static final long LAMBDA_METHOD = 1L<<49; //MethodSymbols only + @Use({FlagTarget.METHOD}) + public static final long LAMBDA_METHOD = 1L<<49; /** * Flag that marks a synthetic local capture field in a local/anon class */ - public static final long LOCAL_CAPTURE_FIELD = 1L<<49; //VarSymbols only + @Use({FlagTarget.VARIABLE}) + public static final long LOCAL_CAPTURE_FIELD = 1L<<49; /** * Flag to control recursion in TransTypes */ + @Use({FlagTarget.CLASS}) public static final long TYPE_TRANSLATED = 1L<<50; /** * Flag to indicate class symbol is for module-info */ + @Use({FlagTarget.CLASS}) public static final long MODULE = 1L<<51; /** * Flag to indicate the given ModuleSymbol is an automatic module. */ - public static final long AUTOMATIC_MODULE = 1L<<52; //ModuleSymbols only + @Use({FlagTarget.MODULE}) + public static final long AUTOMATIC_MODULE = 1L<<52; /** * Flag to indicate the given PackageSymbol contains any non-.java and non-.class resources. */ - public static final long HAS_RESOURCE = 1L<<52; //PackageSymbols only + @Use({FlagTarget.PACKAGE}) + public static final long HAS_RESOURCE = 1L<<52; /** * Flag to indicate the given ParamSymbol has a user-friendly name filled. */ - public static final long NAME_FILLED = 1L<<52; //ParamSymbols only + @Use({FlagTarget.VARIABLE}) //ParamSymbols only + public static final long NAME_FILLED = 1L<<52; /** * Flag to indicate the given ModuleSymbol is a system module. */ - public static final long SYSTEM_MODULE = 1L<<53; //ModuleSymbols only + @Use({FlagTarget.MODULE}) + public static final long SYSTEM_MODULE = 1L<<53; /** * Flag to indicate the given ClassSymbol is a value based. */ - public static final long VALUE_BASED = 1L<<53; //ClassSymbols only + @Use({FlagTarget.CLASS}) + public static final long VALUE_BASED = 1L<<53; /** * Flag to indicate the given symbol has a @Deprecated annotation. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long DEPRECATED_ANNOTATION = 1L<<54; /** * Flag to indicate the given symbol has been deprecated and marked for removal. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long DEPRECATED_REMOVAL = 1L<<55; /** * Flag to indicate the API element in question is for a preview API. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long PREVIEW_API = 1L<<56; //any Symbol kind /** * Flag for synthesized default constructors of anonymous classes that have an enclosing expression. */ + @Use({FlagTarget.METHOD}) public static final long ANONCONSTR_BASED = 1L<<57; /** * Flag that marks finalize block as body-only, should not be copied into catch clauses. * Used to implement try-with-resources. */ - public static final long BODY_ONLY_FINALIZE = 1L<<17; //blocks only + @Use({FlagTarget.BLOCK}) + public static final long BODY_ONLY_FINALIZE = 1L<<17; /** * Flag to indicate the API element in question is for a preview API. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long PREVIEW_REFLECTIVE = 1L<<58; //any Symbol kind /** * Flag to indicate the given variable is a match binding variable. */ + @Use({FlagTarget.VARIABLE}) public static final long MATCH_BINDING = 1L<<59; /** * A flag to indicate a match binding variable whose scope extends after the current statement. */ + @Use({FlagTarget.VARIABLE}) public static final long MATCH_BINDING_TO_OUTER = 1L<<60; /** * Flag to indicate that a class is a record. The flag is also used to mark fields that are * part of the state vector of a record and to mark the canonical constructor */ - public static final long RECORD = 1L<<61; // ClassSymbols, MethodSymbols and VarSymbols + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE, FlagTarget.METHOD}) + public static final long RECORD = 1L<<61; /** * Flag to mark a record constructor as a compact one */ - public static final long COMPACT_RECORD_CONSTRUCTOR = 1L<<51; // MethodSymbols only + @Use({FlagTarget.METHOD}) + public static final long COMPACT_RECORD_CONSTRUCTOR = 1L<<51; /** * Flag to mark a record field that was not initialized in the compact constructor */ - public static final long UNINITIALIZED_FIELD= 1L<<51; // VarSymbols only + @Use({FlagTarget.VARIABLE}) + public static final long UNINITIALIZED_FIELD= 1L<<51; /** Flag is set for compiler-generated record members, it could be applied to * accessors and fields */ - public static final int GENERATED_MEMBER = 1<<24; // MethodSymbols and VarSymbols + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE}) + public static final int GENERATED_MEMBER = 1<<24; /** * Flag to indicate restricted method declaration. */ - public static final long RESTRICTED = 1L<<62; // MethodSymbols + @Use({FlagTarget.METHOD}) + public static final long RESTRICTED = 1L<<62; /** * Flag to indicate parameters that require identity. */ - public static final long REQUIRES_IDENTITY = 1L<<62; // VarSymbols (parameters) + @Use({FlagTarget.VARIABLE}) //ParamSymbols only + public static final long REQUIRES_IDENTITY = 1L<<62; /** * Flag to indicate type annotations have been queued for field initializers. */ - public static final long FIELD_INIT_TYPE_ANNOTATIONS_QUEUED = 1L<<53; // VarSymbols + @Use({FlagTarget.VARIABLE}) + public static final long FIELD_INIT_TYPE_ANNOTATIONS_QUEUED = 1L<<53; /** * Flag to indicate that the class/interface was declared with the non-sealed modifier. */ + @Use({FlagTarget.CLASS}) + @CustomToStringValue("non-sealed") public static final long NON_SEALED = 1L<<63; // part of ExtendedStandardFlags, cannot be reused /** @@ -422,6 +507,7 @@ public static String toSource(long flags) { /** Modifier masks. */ + @NotFlag public static final int AccessFlags = PUBLIC | PROTECTED | PRIVATE, LocalClassFlags = FINAL | ABSTRACT | STRICTFP | ENUM | SYNTHETIC, @@ -438,6 +524,7 @@ public static String toSource(long flags) { SYNCHRONIZED | FINAL | STRICTFP, RecordMethodFlags = AccessFlags | ABSTRACT | STATIC | SYNCHRONIZED | FINAL | STRICTFP; + @NotFlag public static final long //NOTE: flags in ExtendedStandardFlags cannot be overlayed across Symbol kinds: ExtendedStandardFlags = (long)StandardFlags | DEFAULT | SEALED | NON_SEALED, @@ -491,91 +578,45 @@ public static boolean isConstant(Symbol.VarSymbol symbol) { return symbol.getConstValue() != null; } + public enum FlagTarget { + /** This flag can appear the JCBlock. + */ + BLOCK, + /** This flag can appear on ClassSymbols. + */ + CLASS, + /** This flag can appear on ModuleSymbols. + */ + MODULE, + /** This flag can appear on PackageSymbols. + */ + PACKAGE, + /** This flag can appear on TypeVarSymbols. + */ + TYPE_VAR, + /** This flag can appear on MethodSymbols. + */ + METHOD, + /** This flag can appear on VarSymbols, includes + * including ParamSymbol, and BindingSymbol. + */ + VARIABLE; + } - public enum Flag { - PUBLIC(Flags.PUBLIC), - PRIVATE(Flags.PRIVATE), - PROTECTED(Flags.PROTECTED), - STATIC(Flags.STATIC), - FINAL(Flags.FINAL), - SYNCHRONIZED(Flags.SYNCHRONIZED), - VOLATILE(Flags.VOLATILE), - TRANSIENT(Flags.TRANSIENT), - NATIVE(Flags.NATIVE), - INTERFACE(Flags.INTERFACE), - ABSTRACT(Flags.ABSTRACT), - DEFAULT(Flags.DEFAULT), - STRICTFP(Flags.STRICTFP), - BRIDGE(Flags.BRIDGE), - SYNTHETIC(Flags.SYNTHETIC), - ANNOTATION(Flags.ANNOTATION), - DEPRECATED(Flags.DEPRECATED), - HASINIT(Flags.HASINIT), - IMPLICIT_CLASS(Flags.IMPLICIT_CLASS), - BLOCK(Flags.BLOCK), - FROM_SOURCE(Flags.FROM_SOURCE), - ENUM(Flags.ENUM), - MANDATED(Flags.MANDATED), - NOOUTERTHIS(Flags.NOOUTERTHIS), - EXISTS(Flags.EXISTS), - COMPOUND(Flags.COMPOUND), - CLASS_SEEN(Flags.CLASS_SEEN), - SOURCE_SEEN(Flags.SOURCE_SEEN), - LOCKED(Flags.LOCKED), - UNATTRIBUTED(Flags.UNATTRIBUTED), - ANONCONSTR(Flags.ANONCONSTR), - ACYCLIC(Flags.ACYCLIC), - PARAMETER(Flags.PARAMETER), - VARARGS(Flags.VARARGS), - ACYCLIC_ANN(Flags.ACYCLIC_ANN), - GENERATEDCONSTR(Flags.GENERATEDCONSTR), - HYPOTHETICAL(Flags.HYPOTHETICAL), - PROPRIETARY(Flags.PROPRIETARY), - UNION(Flags.UNION), - EFFECTIVELY_FINAL(Flags.EFFECTIVELY_FINAL), - CLASH(Flags.CLASH), - AUXILIARY(Flags.AUXILIARY), - NOT_IN_PROFILE(Flags.NOT_IN_PROFILE), - BAD_OVERRIDE(Flags.BAD_OVERRIDE), - SIGNATURE_POLYMORPHIC(Flags.SIGNATURE_POLYMORPHIC), - THROWS(Flags.THROWS), - LAMBDA_METHOD(Flags.LAMBDA_METHOD), - TYPE_TRANSLATED(Flags.TYPE_TRANSLATED), - MODULE(Flags.MODULE), - AUTOMATIC_MODULE(Flags.AUTOMATIC_MODULE), - SYSTEM_MODULE(Flags.SYSTEM_MODULE), - DEPRECATED_ANNOTATION(Flags.DEPRECATED_ANNOTATION), - DEPRECATED_REMOVAL(Flags.DEPRECATED_REMOVAL), - HAS_RESOURCE(Flags.HAS_RESOURCE), - SEALED(Flags.SEALED), - ANONCONSTR_BASED(Flags.ANONCONSTR_BASED), - NAME_FILLED(Flags.NAME_FILLED), - PREVIEW_API(Flags.PREVIEW_API), - PREVIEW_REFLECTIVE(Flags.PREVIEW_REFLECTIVE), - MATCH_BINDING(Flags.MATCH_BINDING), - MATCH_BINDING_TO_OUTER(Flags.MATCH_BINDING_TO_OUTER), - RECORD(Flags.RECORD), - RECOVERABLE(Flags.RECOVERABLE), - RESTRICTED(Flags.RESTRICTED), - NON_SEALED(Flags.NON_SEALED) { - @Override - public String toString() { - return "non-sealed"; - } - }; - - Flag(long flag) { - this.value = flag; - this.lowercaseName = StringUtils.toLowerCase(name()); - } + @Retention(RetentionPolicy.RUNTIME) + public @interface Use { + public FlagTarget[] value(); + } - @Override - public String toString() { - return lowercaseName; - } + @Retention(RetentionPolicy.RUNTIME) + public @interface NotFlag {} - final long value; - final String lowercaseName; + @Retention(RetentionPolicy.RUNTIME) + public @interface CustomToStringValue { + public String value(); } + @Retention(RetentionPolicy.RUNTIME) + public @interface NoToStringValue { + } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index 47066b24de993..4d0af014d8349 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -62,7 +62,7 @@ import com.sun.tools.javac.code.Directive.RequiresFlag; import com.sun.tools.javac.code.Directive.UsesDirective; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Flags.Flag; +import com.sun.tools.javac.code.FlagsEnum; import com.sun.tools.javac.code.Kinds; import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; @@ -825,7 +825,7 @@ public void visitRequires(JCRequires tree) { } if (tree.isStaticPhase) { if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) { - log.error(tree.pos(), Errors.ModNotAllowedHere(EnumSet.of(Flag.STATIC))); + log.error(tree.pos(), Errors.ModNotAllowedHere(EnumSet.of(FlagsEnum.STATIC))); } else { flags.add(RequiresFlag.STATIC_PHASE); } diff --git a/test/langtools/tools/javac/diags/ArgTypeCompilerFactory.java b/test/langtools/tools/javac/diags/ArgTypeCompilerFactory.java index e40109f99f3d8..823a0f01a7fa4 100644 --- a/test/langtools/tools/javac/diags/ArgTypeCompilerFactory.java +++ b/test/langtools/tools/javac/diags/ArgTypeCompilerFactory.java @@ -29,7 +29,7 @@ import com.sun.tools.javac.api.*; import com.sun.tools.javac.api.DiagnosticFormatter.Configuration.DiagnosticPart; import com.sun.tools.javac.api.Formattable.LocalizedString; -import com.sun.tools.javac.code.Flags.Flag; +import com.sun.tools.javac.code.FlagsEnum; import com.sun.tools.javac.code.Kinds.KindName; import com.sun.tools.javac.code.*; import com.sun.tools.javac.file.*; @@ -303,7 +303,7 @@ static String getArgType(Object o) { return "number"; if (o instanceof String) return "string"; - if (o instanceof Flag) + if (o instanceof FlagsEnum) return "modifier"; if (o instanceof KindName) return "symbol kind"; diff --git a/test/langtools/tools/javac/flags/FlagsTest.java b/test/langtools/tools/javac/flags/FlagsTest.java index 1dcb0606a65b2..a7a9051af331e 100644 --- a/test/langtools/tools/javac/flags/FlagsTest.java +++ b/test/langtools/tools/javac/flags/FlagsTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Google LLC. All rights reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,28 +24,84 @@ /** * @test - * @bug 8211138 + * @bug 8211138 8362885 * @summary Missing Flag enum constants * @library /tools/javac/lib * @modules jdk.compiler/com.sun.tools.javac.code - * @run main FlagsTest + * @compile FlagsTest.java + * @run main/manual FlagsTest */ import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Flags.FlagTarget; +import com.sun.tools.javac.code.Flags.NotFlag; +import com.sun.tools.javac.code.Flags.Use; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class FlagsTest { - public static void main(String[] args) throws IllegalAccessException { + + private static final int U2_SIZE = 16; + + public static void main(String[] args) throws Throwable { + findFreeFlags(); + } + + private static void findFreeFlags() throws Throwable { + Map>> target2Flag2Fields = computeTarget2Flag2Fields(); + + for (FlagTarget target : FlagTarget.values()) { + long freeFlags = ~collectFlags(target2Flag2Fields, target); + + printFreeFlags(target.name(), freeFlags); + } + } + + private static Map>> computeTarget2Flag2Fields() throws Throwable { + Map>> target2Flag2Fields = new HashMap<>(); for (Field f : Flags.class.getFields()) { - if (!Modifier.isStatic(f.getModifiers())) { + if (f.isAnnotationPresent(NotFlag.class)) { continue; } - long flag = ((Number) f.get(null)).longValue(); - try { - Flags.asFlagSet(flag); - } catch (AssertionError e) { - throw new AssertionError("missing Flags enum constant for: " + f.getName(), e); + + Use use = f.getAnnotation(Use.class); + + if (use == null) { + throw new AssertionError("No @Use and no @NotFlag for: " + f.getName()); + } + + long flagValue = ((Number) f.get(null)).longValue(); + + for (FlagTarget target : use.value()) { + target2Flag2Fields.computeIfAbsent(target, _ -> new HashMap<>()) + .computeIfAbsent(flagValue, _ -> new ArrayList<>()) + .add(f); } } + return target2Flag2Fields; + } + + private static void printFreeFlags(String comment, long freeFlags) { + System.err.print("free flags for " + comment + ": "); + for (int bit = U2_SIZE; bit < Long.SIZE; bit++) { //lowest 16 bits are used in classfiles, never suggest adding anything there + if ((freeFlags & (1L << bit)) != 0) { + System.err.print("1L<<" + bit + " "); + } + } + System.err.println(); + } + + private static long collectFlags(Map>> target2Flag2Fields, FlagTarget... forTargets) { + long flags = 0; + + for (FlagTarget target : forTargets) { + for (long used : target2Flag2Fields.get(target).keySet()) { + flags |= used; + } + } + + return flags; } }