8362885: A more formal way to mark javac's Flags that belong to a specific Symbol type only

Reviewed-by: ihse, liach, vromero, mcimadamore, erikj
This commit is contained in:
Jan Lahoda 2025-08-13 08:07:45 +00:00
parent 25480f0011
commit 72e22b4de5
8 changed files with 422 additions and 140 deletions

View file

@ -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, \
))

View file

@ -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<Integer, List<String>> flag2Names = new TreeMap<>();
Map<FlagTarget, Map<Integer, List<String>>> target2FlagBit2Fields = new EnumMap<>(FlagTarget.class);
Map<String, String> customToString = new HashMap<>();
Set<String> 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<FlagTarget, Map<Integer, List<String>>> targetAndFlag : target2FlagBit2Fields.entrySet()) {
for (Entry<Integer, List<String>> 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<Integer, List<String>> 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;
}
}

View file

@ -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 interface 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"),

View file

@ -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)
################################################################################

View file

@ -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 java.util.stream.Collectors;
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 @@ public class Flags {
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 class Flags {
return buf.toString();
}
public static EnumSet<Flag> asFlagSet(long flags) {
EnumSet<Flag> flagSet = EnumSet.noneOf(Flag.class);
for (Flag flag : Flag.values()) {
if ((flags & flag.value) != 0) {
public static EnumSet<FlagsEnum> asFlagSet(long flags) {
EnumSet<FlagsEnum> 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 class 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 class 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 class 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 class 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 class Flags {
/** Modifier masks.
*/
@NotFlag
public static final int
AccessFlags = PUBLIC | PROTECTED | PRIVATE,
LocalClassFlags = FINAL | ABSTRACT | STRICTFP | ENUM | SYNTHETIC,
@ -438,6 +524,7 @@ public class 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 class Flags {
return symbol.getConstValue() != null;
}
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());
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;
}
@Override
public String toString() {
return lowercaseName;
@Retention(RetentionPolicy.RUNTIME)
public @interface Use {
public FlagTarget[] value();
}
final long value;
final String lowercaseName;
@Retention(RetentionPolicy.RUNTIME)
public @interface NotFlag {}
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomToStringValue {
public String value();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface NoToStringValue {
}
}

View file

@ -62,7 +62,7 @@ import com.sun.tools.javac.code.Directive.RequiresDirective;
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 class Modules extends JCTree.Visitor {
}
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);
}

View file

@ -29,7 +29,7 @@ import javax.tools.*;
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 @@ class ArgTypeCompilerFactory implements Example.Compiler.Factory {
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";

View file

@ -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<FlagTarget, Map<Long, List<Field>>> target2Flag2Fields = computeTarget2Flag2Fields();
for (FlagTarget target : FlagTarget.values()) {
long freeFlags = ~collectFlags(target2Flag2Fields, target);
printFreeFlags(target.name(), freeFlags);
}
}
private static Map<FlagTarget, Map<Long, List<Field>>> computeTarget2Flag2Fields() throws Throwable {
Map<FlagTarget, Map<Long, List<Field>>> 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<FlagTarget, Map<Long, List<Field>>> target2Flag2Fields, FlagTarget... forTargets) {
long flags = 0;
for (FlagTarget target : forTargets) {
for (long used : target2Flag2Fields.get(target).keySet()) {
flags |= used;
}
}
return flags;
}
}