From 9c7007ffa802ab08363cbf51adaef959cfe91a9b Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 23 Jul 2025 12:07:38 +0200 Subject: [PATCH 1/9] 8362885: A more formal way to mark javac's Flags that belong to a specific Symbol type only --- make/ToolsLangtools.gmk | 2 +- .../tools/flagsgenerator/FlagsGenerator.java | 125 +++++++++ .../propertiesparser/parser/MessageType.java | 4 +- make/modules/jdk.compiler/Gensrc.gmk | 35 ++- .../com/sun/tools/javac/code/Flags.java | 237 ++++++++++-------- .../com/sun/tools/javac/comp/Modules.java | 4 +- .../tools/javac/flags/FlagsTest.java | 89 ++++++- 7 files changed, 371 insertions(+), 125 deletions(-) create mode 100644 make/langtools/tools/flagsgenerator/FlagsGenerator.java 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..1a9e71e3bf109 --- /dev/null +++ b/make/langtools/tools/flagsgenerator/FlagsGenerator.java @@ -0,0 +1,125 @@ +/* + * 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.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +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 customToString = new HashMap<>(); + + 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(); + + flag2Names.computeIfAbsent(63 - Long.numberOfLeadingZeros(flagValue), _ -> new ArrayList<>()) + .add(flagName); + } + + + case "com.sun.tools.javac.code.Flags.CustomToStringValue" -> { + customToString.put(flagName, (String) valueOfValueAttribute(am)); + } + } + } + } + + 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() + .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(); + } +} 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..b5ec153256de7 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,28 @@ $(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..fe47d6c71e2a5 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,41 +74,62 @@ public static EnumSet asFlagSet(long flags) { /* Standard Java flags. */ + @Use({FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) public static final int PUBLIC = 1; + @Use({FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) public static final int PRIVATE = 1<<1; + @Use({FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) public static final int PROTECTED = 1<<2; + @Use({FlagTarget.BLOCK, FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) public static final int STATIC = 1<<3; + @Use({FlagTarget.METHOD, FlagTarget.TYPE, 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.TYPE}) public static final int INTERFACE = 1<<9; + @Use({FlagTarget.METHOD, FlagTarget.TYPE}) public static final int ABSTRACT = 1<<10; + @Use({FlagTarget.TYPE}) public static final int STRICTFP = 1<<11; /* Flag that marks a symbol synthetic, added in classfile v49.0. */ + @Use({FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) public static final int SYNTHETIC = 1<<12; /** Flag that marks attribute interfaces, added in classfile v49.0. */ + @Use({FlagTarget.TYPE}) public static final int ANNOTATION = 1<<13; /** An enumeration type or an enumeration constant, added in * classfile v49.0. */ + @Use({FlagTarget.TYPE}) public static final int ENUM = 1<<14; /** Added in SE8, represents constructs implicitly declared in source. */ + @Use({FlagTarget.TYPE, 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. + @NotFlag public static final int ACC_SUPER = 0x0020; + @NotFlag public static final int ACC_BRIDGE = 0x0040; + @NotFlag public static final int ACC_VARARGS = 0x0080; + @NotFlag public static final int ACC_MODULE = 0x8000; /* *************************************** @@ -116,24 +138,29 @@ public static EnumSet asFlagSet(long flags) { /** Flag is set if symbol is deprecated. See also DEPRECATED_REMOVAL. */ + @Use({FlagTarget.METHOD, FlagTarget.TYPE, 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.TYPE}) 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. */ + @Use({FlagTarget.TYPE}) public static final int FROM_SOURCE = 1<<21; //ClassSymbols /** Flag is set for nested classes that do not access instance members @@ -143,25 +170,30 @@ public static EnumSet asFlagSet(long flags) { * todo: use this value for optimizing away this$n parameters in * other cases. */ + @Use({FlagTarget.TYPE, 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.TYPE}) public static final int EXISTS = 1<<23; /** Flag is set for compiler-generated compound classes * representing multiple variable bounds */ + @Use({FlagTarget.TYPE}) public static final int COMPOUND = 1<<24; /** Flag is set for class symbols if a class file was found for this class. */ + @Use({FlagTarget.TYPE}) 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.TYPE}) public static final int SOURCE_SEEN = 1<<26; /* State flags (are reset during compilation). @@ -172,242 +204,291 @@ public static EnumSet asFlagSet(long flags) { * relations. Similarly for constructor call cycle detection in * Attr. */ + @Use({FlagTarget.METHOD, FlagTarget.TYPE}) 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.TYPE}) 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. */ + @Use({FlagTarget.TYPE}) public static final int SUPER_OWNER_ATTRIBUTED = 1<<29; //ClassSymbols /** Flag for class symbols to indicate it has been checked and found * acyclic. */ + @Use({FlagTarget.METHOD, FlagTarget.TYPE}) 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}) public static final long VARARGS = 1L<<34; /** Flag for annotation type symbols to indicate it has been * checked and found acyclic. */ + @Use({FlagTarget.TYPE}) 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.TYPE}) 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.TYPE}) 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.METHOD, FlagTarget.TYPE}) 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.TYPE}) public static final long AUXILIARY = 1L<<44; /** * Flag that marks that a symbol is not available in the current profile */ + @Use({FlagTarget.TYPE}) 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}) public static final long THROWS = 1L<<47; /** * Flag to indicate sealed class/interface declaration. */ + @Use({FlagTarget.TYPE}) 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.TYPE}) public static final long TYPE_TRANSLATED = 1L<<50; /** * Flag to indicate class symbol is for module-info */ + @Use({FlagTarget.TYPE}) 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.TYPE}) //ModuleSymbols only + 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.TYPE}) //PackageSymbols only + 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.TYPE}) //ModuleSymbols only + 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.TYPE}) //ClassSymbols only + public static final long VALUE_BASED = 1L<<53; /** * Flag to indicate the given symbol has a @Deprecated annotation. */ + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE, FlagTarget.TYPE}) public static final long DEPRECATED_ANNOTATION = 1L<<54; /** * Flag to indicate the given symbol has been deprecated and marked for removal. */ + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE, FlagTarget.TYPE}) public static final long DEPRECATED_REMOVAL = 1L<<55; /** * Flag to indicate the API element in question is for a preview API. */ + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE, FlagTarget.TYPE}) 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.TYPE, FlagTarget.VARIABLE, FlagTarget.METHOD}) 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.TYPE, 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.TYPE}) + @CustomToStringValue("non-sealed") public static final long NON_SEALED = 1L<<63; // part of ExtendedStandardFlags, cannot be reused /** @@ -422,6 +503,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 +520,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 +574,33 @@ 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 TypeSymbols, includes + * ModuleSymbols, ClassSymbol, PackageSymbol and TypeVarSymbol. + */ + TYPE, + /** 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(); } - } 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/flags/FlagsTest.java b/test/langtools/tools/javac/flags/FlagsTest.java index 1dcb0606a65b2..a1deccae76152 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,98 @@ /** * @test - * @bug 8211138 + * @bug 8211138 8362885 * @summary Missing Flag enum constants * @library /tools/javac/lib * @modules jdk.compiler/com.sun.tools.javac.code + * @compile FlagsTest.java * @run main 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; +import java.util.Map.Entry; public class FlagsTest { - public static void main(String[] args) throws IllegalAccessException { + + public static void main(String[] args) throws Throwable { + verifyFlagsNonOverlapping(); + findFreeFlags(); + } + + private static void verifyFlagsNonOverlapping() throws Throwable { + Map>> target2Flag2Fields = computeTarget2Flag2Fields(); + + for (Entry>> targetAndFlag : target2Flag2Fields.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()); + } + } + } + } + + private static void findFreeFlags() throws Throwable { + Map>> target2Flag2Fields = computeTarget2Flag2Fields(); + + long freeTypeFlags = ~collectFlags(target2Flag2Fields, FlagTarget.TYPE); + long freeMethodFlags = ~collectFlags(target2Flag2Fields, FlagTarget.METHOD); + long freeVariableFlags = ~collectFlags(target2Flag2Fields, FlagTarget.VARIABLE); + + printFreeFlags("TYPE", freeTypeFlags); + printFreeFlags("METHOD", freeMethodFlags); + printFreeFlags("VARIABLE", freeVariableFlags); + } + + private static Map>> computeTarget2Flag2Fields() throws IllegalArgumentException, IllegalAccessException, AssertionError { + 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 = 16; bit < 64; 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; } } From 0ab2ba48e971a7dc37491ccf961277343563fb65 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 23 Jul 2025 16:49:53 +0200 Subject: [PATCH 2/9] Fixing tests. --- .../share/classes/com/sun/tools/javac/code/Flags.java | 4 ++-- test/langtools/tools/javac/diags/ArgTypeCompilerFactory.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 fe47d6c71e2a5..232e1e799341a 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 @@ -366,7 +366,7 @@ public static EnumSet asFlagSet(long flags) { * Flag to indicate the given ModuleSymbol is an automatic module. */ @Use({FlagTarget.TYPE}) //ModuleSymbols only - public static final long AUTOMATIC_MODULE = 1L<<52; + public static final long AUTOMATIC_MODULE = 1L<<57; /** * Flag to indicate the given PackageSymbol contains any non-.java and non-.class resources. @@ -384,7 +384,7 @@ public static EnumSet asFlagSet(long flags) { * Flag to indicate the given ModuleSymbol is a system module. */ @Use({FlagTarget.TYPE}) //ModuleSymbols only - public static final long SYSTEM_MODULE = 1L<<53; + public static final long SYSTEM_MODULE = 1L<<59; /** * Flag to indicate the given ClassSymbol is a value based. 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"; From 5bc1d6dcd37f7ccf369a0c7ab1ab512cab737520 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 25 Jul 2025 09:54:05 +0200 Subject: [PATCH 3/9] Reflecting review feedback: - when conflict is detected, the generator fails - adding runtime checks - using constants for number of bits --- .../tools/flagsgenerator/FlagsGenerator.java | 57 +++++++++++++++++-- .../com/sun/tools/javac/code/Flags.java | 6 +- .../com/sun/tools/javac/comp/Lower.java | 15 +++++ .../com/sun/tools/javac/jvm/ClassWriter.java | 9 +++ .../tools/javac/flags/FlagsTest.java | 22 ++----- 5 files changed, 85 insertions(+), 24 deletions(-) diff --git a/make/langtools/tools/flagsgenerator/FlagsGenerator.java b/make/langtools/tools/flagsgenerator/FlagsGenerator.java index 1a9e71e3bf109..37f7948b4e5bd 100644 --- a/make/langtools/tools/flagsgenerator/FlagsGenerator.java +++ b/make/langtools/tools/flagsgenerator/FlagsGenerator.java @@ -59,6 +59,7 @@ public static void main(String... args) throws IOException { TypeElement clazz = (TypeElement) trees.getElement(new TreePath(new TreePath(cut), cut.getTypeDecls().get(0))); Map> flag2Names = new TreeMap<>(); + Map>> target2FlagBit2Fields = new HashMap<>(); Map customToString = new HashMap<>(); for (VariableElement field : ElementFilter.fieldsIn(clazz.getEnclosedElements())) { @@ -67,12 +68,18 @@ public static void main(String... args) throws IOException { 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(63 - Long.numberOfLeadingZeros(flagValue), _ -> new ArrayList<>()) + 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)); } @@ -80,10 +87,23 @@ public static void main(String... args) throws IOException { } } + //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; + import com.sun.tools.javac.util.Assert; + public enum FlagsEnum { """); for (Entry> e : flag2Names.entrySet()) { @@ -97,6 +117,10 @@ public enum FlagsEnum { out.println(""" ; + public static final long MASK_TYPE_FLAGS = ${TYPE_FLAGS}; + public static final long MASK_METHOD_FLAGS = ${METHOD_FLAGS}; + public static final long MASK_VARIABLE_FLAGS = ${VARIABLE_FLAGS}; + private final long value; private final String toString; private FlagsEnum(long value, String toString) { @@ -109,12 +133,30 @@ public long value() { public String toString() { return toString; } + public static void assertNoUnexpectedFlags(long flags, long mask) { + Assert.check((flags & ~mask) == 0, + () -> "Unexpected flags: 0x" + Long.toHexString(flags & ~mask) + "L (" + + Flags.asFlagSet(flags & ~mask) + ")"); + } } - """); + """.replace("${METHOD_FLAGS}", getMask(target2FlagBit2Fields, FlagTarget.METHOD)) + .replace("${TYPE_FLAGS}", getMask(target2FlagBit2Fields, FlagTarget.TYPE)) + .replace("${VARIABLE_FLAGS}", getMask(target2FlagBit2Fields, FlagTarget.VARIABLE))); } } } + private static String getMask(Map>> target2FlagBit2Fields, + FlagTarget target) { + long mask = target2FlagBit2Fields.get(target) + .keySet() + .stream() + .mapToLong(bit -> 1L << bit) + .reduce(0, (l, r) -> l | r); + + return "0x" + Long.toHexString(mask) + "L"; + } + private static Object valueOfValueAttribute(AnnotationMirror am) { return am.getElementValues() .values() @@ -122,4 +164,11 @@ private static Object valueOfValueAttribute(AnnotationMirror am) { .next() .getValue(); } + + public enum FlagTarget { + BLOCK, + METHOD, + TYPE, + VARIABLE; + } } 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 232e1e799341a..ed00e378f66a9 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 @@ -96,7 +96,7 @@ public static EnumSet asFlagSet(long flags) { public static final int INTERFACE = 1<<9; @Use({FlagTarget.METHOD, FlagTarget.TYPE}) public static final int ABSTRACT = 1<<10; - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.METHOD, FlagTarget.TYPE}) public static final int STRICTFP = 1<<11; /* Flag that marks a symbol synthetic, added in classfile v49.0. */ @@ -109,7 +109,7 @@ public static EnumSet asFlagSet(long flags) { /** An enumeration type or an enumeration constant, added in * classfile v49.0. */ - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.TYPE, FlagTarget.VARIABLE}) public static final int ENUM = 1<<14; /** Added in SE8, represents constructs implicitly declared in source. */ @@ -242,7 +242,7 @@ public static EnumSet asFlagSet(long flags) { /** Flag that marks varargs methods. */ - @Use({FlagTarget.METHOD}) + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final long VARARGS = 1L<<34; /** Flag for annotation type symbols to indicate it has been diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index aee7f0afe3975..032ae5dfd410f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -2127,6 +2127,9 @@ private boolean needPackageInfoClass(JCPackageDecl pd) { } public void visitModuleDef(JCModuleDecl tree) { + FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, + FlagsEnum.MASK_TYPE_FLAGS); + ModuleSymbol msym = tree.sym; ClassSymbol c = msym.module_info; c.setAttributes(msym); @@ -2145,6 +2148,9 @@ private void createInfoClass(List annots, ClassSymbol c) { } public void visitClassDef(JCClassDecl tree) { + FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, + FlagsEnum.MASK_TYPE_FLAGS); + Env prevEnv = attrEnv; ClassSymbol currentClassPrev = currentClass; MethodSymbol currentMethodSymPrev = currentMethodSym; @@ -2628,6 +2634,9 @@ JCFieldAccess makeIndyQualifier( } public void visitMethodDef(JCMethodDecl tree) { + FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, + FlagsEnum.MASK_METHOD_FLAGS); + if (tree.name == names.init && (currentClass.flags_field&ENUM) != 0) { // Add "String $enum$name, int $enum$ordinal" to the beginning of the // argument list for each constructor of an enum. @@ -3715,6 +3724,9 @@ private void visitIterableForeachLoop(JCEnhancedForLoop tree) { } public void visitVarDef(JCVariableDecl tree) { + FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, + FlagsEnum.MASK_VARIABLE_FLAGS); + MethodSymbol oldMethodSym = currentMethodSym; int prevVariableIndex = variableIndex; tree.mods = translate(tree.mods); @@ -4349,6 +4361,9 @@ public void visitTry(JCTry tree) { * @param cdef The tree representing the class definition. */ public List translateTopLevelClass(Env env, JCTree cdef, TreeMaker make) { + FlagsEnum.assertNoUnexpectedFlags(env.toplevel.packge.flags_field, + FlagsEnum.MASK_TYPE_FLAGS); + ListBuffer translated = null; try { attrEnv = env; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index a24184e990b3d..e8c36785f7614 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -973,6 +973,9 @@ void writeBootstrapMethods() { /** Write field symbol, entering all references into constant pool. */ void writeField(VarSymbol v) { + FlagsEnum.assertNoUnexpectedFlags(v.flags_field, + FlagsEnum.MASK_VARIABLE_FLAGS); + int flags = adjustFlags(v.flags()); databuf.appendChar(flags); if (dumpFieldModifiers) { @@ -998,6 +1001,9 @@ void writeField(VarSymbol v) { /** Write method symbol, entering all references into constant pool. */ void writeMethod(MethodSymbol m) { + FlagsEnum.assertNoUnexpectedFlags(m.flags_field, + FlagsEnum.MASK_METHOD_FLAGS); + int flags = adjustFlags(m.flags()); databuf.appendChar(flags); if (dumpMethodModifiers) { @@ -1535,6 +1541,9 @@ void writeMethods(Scope s) { public JavaFileObject writeClass(ClassSymbol c) throws IOException, PoolOverflow, StringOverflow { + FlagsEnum.assertNoUnexpectedFlags(c.flags_field, + FlagsEnum.MASK_TYPE_FLAGS); + String name = (c.owner.kind == MDL ? c.name : c.flatname).toString(); Location outLocn; if (multiModuleMode) { diff --git a/test/langtools/tools/javac/flags/FlagsTest.java b/test/langtools/tools/javac/flags/FlagsTest.java index a1deccae76152..766c56b41d128 100644 --- a/test/langtools/tools/javac/flags/FlagsTest.java +++ b/test/langtools/tools/javac/flags/FlagsTest.java @@ -29,7 +29,7 @@ * @library /tools/javac/lib * @modules jdk.compiler/com.sun.tools.javac.code * @compile FlagsTest.java - * @run main FlagsTest + * @run main/manual FlagsTest */ import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Flags.FlagTarget; @@ -40,27 +40,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; public class FlagsTest { + private static final int U2_SIZE = 16; + public static void main(String[] args) throws Throwable { - verifyFlagsNonOverlapping(); findFreeFlags(); } - private static void verifyFlagsNonOverlapping() throws Throwable { - Map>> target2Flag2Fields = computeTarget2Flag2Fields(); - - for (Entry>> targetAndFlag : target2Flag2Fields.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()); - } - } - } - } - private static void findFreeFlags() throws Throwable { Map>> target2Flag2Fields = computeTarget2Flag2Fields(); @@ -73,7 +61,7 @@ private static void findFreeFlags() throws Throwable { printFreeFlags("VARIABLE", freeVariableFlags); } - private static Map>> computeTarget2Flag2Fields() throws IllegalArgumentException, IllegalAccessException, AssertionError { + private static Map>> computeTarget2Flag2Fields() throws Throwable { Map>> target2Flag2Fields = new HashMap<>(); for (Field f : Flags.class.getFields()) { if (f.isAnnotationPresent(NotFlag.class)) { @@ -99,7 +87,7 @@ private static Map>> computeTarget2Flag2Fields private static void printFreeFlags(String comment, long freeFlags) { System.err.print("free flags for " + comment + ": "); - for (int bit = 16; bit < 64; bit++) { //lowest 16 bits are used in classfiles, never suggest adding anything there + 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 + " "); } From b0fbd9195fb2d2935d71b1a32628a412c0f984ec Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 25 Jul 2025 12:14:59 +0200 Subject: [PATCH 4/9] Review feedback: marking ACC flags with the @Use annotation, forces split of the TYPE target into CLASS/MODULE/PACKAGE/TYPE_VARIABLE. --- .../tools/flagsgenerator/FlagsGenerator.java | 18 ++- .../com/sun/tools/javac/code/Flags.java | 126 ++++++++++-------- .../com/sun/tools/javac/comp/Lower.java | 14 +- .../com/sun/tools/javac/jvm/ClassWriter.java | 2 +- .../tools/javac/flags/FlagsTest.java | 10 +- 5 files changed, 94 insertions(+), 76 deletions(-) diff --git a/make/langtools/tools/flagsgenerator/FlagsGenerator.java b/make/langtools/tools/flagsgenerator/FlagsGenerator.java index 37f7948b4e5bd..a0ca8c08304e0 100644 --- a/make/langtools/tools/flagsgenerator/FlagsGenerator.java +++ b/make/langtools/tools/flagsgenerator/FlagsGenerator.java @@ -33,6 +33,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -98,6 +99,10 @@ public static void main(String... args) throws IOException { } } + String masks = Arrays.stream(FlagTarget.values()) + .map(target -> " public static final long MASK_" + target.name() + "_FLAGS = " + getMask(target2FlagBit2Fields, target) + ";") + .collect(Collectors.joining("\n")); + try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(Paths.get(args[1])))) { out.println(""" package com.sun.tools.javac.code; @@ -117,9 +122,7 @@ public enum FlagsEnum { out.println(""" ; - public static final long MASK_TYPE_FLAGS = ${TYPE_FLAGS}; - public static final long MASK_METHOD_FLAGS = ${METHOD_FLAGS}; - public static final long MASK_VARIABLE_FLAGS = ${VARIABLE_FLAGS}; + ${masks} private final long value; private final String toString; @@ -139,9 +142,7 @@ public static void assertNoUnexpectedFlags(long flags, long mask) { Flags.asFlagSet(flags & ~mask) + ")"); } } - """.replace("${METHOD_FLAGS}", getMask(target2FlagBit2Fields, FlagTarget.METHOD)) - .replace("${TYPE_FLAGS}", getMask(target2FlagBit2Fields, FlagTarget.TYPE)) - .replace("${VARIABLE_FLAGS}", getMask(target2FlagBit2Fields, FlagTarget.VARIABLE))); + """.replace("${masks}", masks)); } } } @@ -167,8 +168,11 @@ private static Object valueOfValueAttribute(AnnotationMirror am) { public enum FlagTarget { BLOCK, + CLASS, METHOD, - TYPE, + MODULE, + PACKAGE, + TYPE_VAR, VARIABLE; } } 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 ed00e378f66a9..a1f5b8121050e 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 @@ -74,15 +74,15 @@ public static EnumSet asFlagSet(long flags) { /* Standard Java flags. */ - @Use({FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PUBLIC = 1; - @Use({FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PRIVATE = 1<<1; - @Use({FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PROTECTED = 1<<2; - @Use({FlagTarget.BLOCK, FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) + @Use({FlagTarget.BLOCK, FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int STATIC = 1<<3; - @Use({FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int FINAL = 1<<4; @Use({FlagTarget.METHOD}) public static final int SYNCHRONIZED = 1<<5; @@ -92,28 +92,28 @@ public static EnumSet asFlagSet(long flags) { public static final int TRANSIENT = 1<<7; @Use({FlagTarget.METHOD}) public static final int NATIVE = 1<<8; - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.CLASS}) public static final int INTERFACE = 1<<9; - @Use({FlagTarget.METHOD, FlagTarget.TYPE}) + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final int ABSTRACT = 1<<10; - @Use({FlagTarget.METHOD, FlagTarget.TYPE}) + @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.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) + @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.TYPE}) + @Use({FlagTarget.CLASS}) public static final int ANNOTATION = 1<<13; /** An enumeration type or an enumeration constant, added in * classfile v49.0. */ - @Use({FlagTarget.TYPE, FlagTarget.VARIABLE}) + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE}) public static final int ENUM = 1<<14; /** Added in SE8, represents constructs implicitly declared in source. */ - @Use({FlagTarget.TYPE, FlagTarget.VARIABLE}) + @Use({FlagTarget.MODULE, FlagTarget.VARIABLE}) public static final int MANDATED = 1<<15; @NotFlag @@ -123,14 +123,14 @@ public static EnumSet asFlagSet(long flags) { // bit positions, we translate them when reading and writing class // files into unique bits positions: ACC_SYNTHETIC <-> SYNTHETIC, // for example. - @NotFlag - public static final int ACC_SUPER = 0x0020; - @NotFlag - public static final int ACC_BRIDGE = 0x0040; - @NotFlag - public static final int ACC_VARARGS = 0x0080; - @NotFlag - public static final int ACC_MODULE = 0x8000; + @Use({FlagTarget.CLASS}) + public static final int ACC_SUPER = 1<<5; + @Use({FlagTarget.METHOD}) + public static final int ACC_BRIDGE = 1<<6; + @Use({FlagTarget.METHOD}) + public static final int ACC_VARARGS = 1<<7; + @Use({FlagTarget.CLASS}) + public static final int ACC_MODULE = 1<<15; /* *************************************** * Internal compiler flags (no bits in the lower 16). @@ -138,7 +138,7 @@ public static EnumSet asFlagSet(long flags) { /** Flag is set if symbol is deprecated. See also DEPRECATED_REMOVAL. */ - @Use({FlagTarget.METHOD, FlagTarget.TYPE, FlagTarget.VARIABLE}) + @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 @@ -149,7 +149,7 @@ public static EnumSet asFlagSet(long flags) { /** Class is an implicitly declared top level class. */ - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.CLASS}) public static final int IMPLICIT_CLASS = 1<<19; /** Flag is set for compiler-generated anonymous method symbols @@ -160,8 +160,8 @@ public static EnumSet asFlagSet(long flags) { /** Flag is set for ClassSymbols that are being compiled from source. */ - @Use({FlagTarget.TYPE}) - 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 @@ -170,30 +170,30 @@ public static EnumSet asFlagSet(long flags) { * todo: use this value for optimizing away this$n parameters in * other cases. */ - @Use({FlagTarget.TYPE, FlagTarget.VARIABLE}) + @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.TYPE}) + @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.TYPE}) + @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.TYPE}) + @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.TYPE}) + @Use({FlagTarget.CLASS}) public static final int SOURCE_SEEN = 1<<26; /* State flags (are reset during compilation). @@ -204,13 +204,13 @@ public static EnumSet asFlagSet(long flags) { * relations. Similarly for constructor call cycle detection in * Attr. */ - @Use({FlagTarget.METHOD, FlagTarget.TYPE}) + @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.TYPE}) + @Use({FlagTarget.CLASS}) public static final int UNATTRIBUTED = 1<<28; /** Flag for synthesized default constructors of anonymous classes. @@ -221,13 +221,13 @@ public static EnumSet asFlagSet(long flags) { /** * Flag to indicate the superclasses of this ClassSymbol has been attributed. */ - @Use({FlagTarget.TYPE}) - 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.METHOD, FlagTarget.TYPE}) + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.TYPE_VAR}) public static final int ACYCLIC = 1<<30; /** Flag that marks bridge methods. @@ -248,7 +248,7 @@ public static EnumSet asFlagSet(long flags) { /** Flag for annotation type symbols to indicate it has been * checked and found acyclic. */ - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.CLASS}) public static final long ACYCLIC_ANN = 1L<<35; /** Flag that marks a generated default constructor. @@ -266,7 +266,7 @@ public static EnumSet asFlagSet(long flags) { /** * Flag that marks an internal proprietary class. */ - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.CLASS}) public static final long PROPRIETARY = 1L<<38; /** @@ -279,7 +279,7 @@ public static EnumSet asFlagSet(long flags) { * Flags an erroneous TypeSymbol as viable for recovery. * TypeSymbols only. */ - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.CLASS, FlagTarget.TYPE_VAR}) public static final long RECOVERABLE = 1L<<40; /** @@ -298,20 +298,20 @@ public static EnumSet asFlagSet(long flags) { /** * Flag that marks either a default method or an interface containing default methods. */ - @Use({FlagTarget.METHOD, FlagTarget.TYPE}) + @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.TYPE}) + @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.TYPE}) + @Use({FlagTarget.CLASS}) public static final long NOT_IN_PROFILE = 1L<<45; /** @@ -329,13 +329,13 @@ public static EnumSet asFlagSet(long flags) { /** * Flag that indicates that an inference variable is used in a 'throws' clause. */ - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.TYPE_VAR}) public static final long THROWS = 1L<<47; /** * Flag to indicate sealed class/interface declaration. */ - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.CLASS}) public static final long SEALED = 1L<<48; // part of ExtendedStandardFlags, cannot be reused /** @@ -353,25 +353,25 @@ public static EnumSet asFlagSet(long flags) { /** * Flag to control recursion in TransTypes */ - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.CLASS}) public static final long TYPE_TRANSLATED = 1L<<50; /** * Flag to indicate class symbol is for module-info */ - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.CLASS}) public static final long MODULE = 1L<<51; /** * Flag to indicate the given ModuleSymbol is an automatic module. */ - @Use({FlagTarget.TYPE}) //ModuleSymbols only - public static final long AUTOMATIC_MODULE = 1L<<57; + @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. */ - @Use({FlagTarget.TYPE}) //PackageSymbols only + @Use({FlagTarget.PACKAGE}) public static final long HAS_RESOURCE = 1L<<52; /** @@ -383,31 +383,31 @@ public static EnumSet asFlagSet(long flags) { /** * Flag to indicate the given ModuleSymbol is a system module. */ - @Use({FlagTarget.TYPE}) //ModuleSymbols only - public static final long SYSTEM_MODULE = 1L<<59; + @Use({FlagTarget.MODULE}) + public static final long SYSTEM_MODULE = 1L<<53; /** * Flag to indicate the given ClassSymbol is a value based. */ - @Use({FlagTarget.TYPE}) //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.METHOD, FlagTarget.VARIABLE, FlagTarget.TYPE}) + @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.METHOD, FlagTarget.VARIABLE, FlagTarget.TYPE}) + @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.METHOD, FlagTarget.VARIABLE, FlagTarget.TYPE}) + @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 /** @@ -426,7 +426,7 @@ public static EnumSet asFlagSet(long flags) { /** * Flag to indicate the API element in question is for a preview API. */ - @Use({FlagTarget.TYPE, FlagTarget.VARIABLE, FlagTarget.METHOD}) + @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 /** @@ -445,7 +445,7 @@ public static EnumSet asFlagSet(long flags) { * 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 */ - @Use({FlagTarget.TYPE, FlagTarget.VARIABLE, FlagTarget.METHOD}) + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE, FlagTarget.METHOD}) public static final long RECORD = 1L<<61; /** @@ -487,7 +487,7 @@ public static EnumSet asFlagSet(long flags) { /** * Flag to indicate that the class/interface was declared with the non-sealed modifier. */ - @Use({FlagTarget.TYPE}) + @Use({FlagTarget.CLASS}) @CustomToStringValue("non-sealed") public static final long NON_SEALED = 1L<<63; // part of ExtendedStandardFlags, cannot be reused @@ -578,10 +578,18 @@ public enum FlagTarget { /** This flag can appear the JCBlock. */ BLOCK, - /** This flag can appear on TypeSymbols, includes - * ModuleSymbols, ClassSymbol, PackageSymbol and TypeVarSymbol. + /** 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, + TYPE_VAR, /** This flag can appear on MethodSymbols. */ METHOD, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index 032ae5dfd410f..2cabac7fc3c1f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -2128,7 +2128,7 @@ private boolean needPackageInfoClass(JCPackageDecl pd) { public void visitModuleDef(JCModuleDecl tree) { FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, - FlagsEnum.MASK_TYPE_FLAGS); + FlagsEnum.MASK_MODULE_FLAGS); ModuleSymbol msym = tree.sym; ClassSymbol c = msym.module_info; @@ -2149,7 +2149,11 @@ private void createInfoClass(List annots, ClassSymbol c) { public void visitClassDef(JCClassDecl tree) { FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, - FlagsEnum.MASK_TYPE_FLAGS); + FlagsEnum.MASK_CLASS_FLAGS); + tree.sym + .getTypeParameters() + .forEach(tv -> FlagsEnum.assertNoUnexpectedFlags(tv.flags_field, + FlagsEnum.MASK_TYPE_VAR_FLAGS)); Env prevEnv = attrEnv; ClassSymbol currentClassPrev = currentClass; @@ -2636,6 +2640,10 @@ JCFieldAccess makeIndyQualifier( public void visitMethodDef(JCMethodDecl tree) { FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, FlagsEnum.MASK_METHOD_FLAGS); + tree.sym + .getTypeParameters() + .forEach(tv -> FlagsEnum.assertNoUnexpectedFlags(tv.flags_field, + FlagsEnum.MASK_TYPE_VAR_FLAGS)); if (tree.name == names.init && (currentClass.flags_field&ENUM) != 0) { // Add "String $enum$name, int $enum$ordinal" to the beginning of the @@ -4362,7 +4370,7 @@ public void visitTry(JCTry tree) { */ public List translateTopLevelClass(Env env, JCTree cdef, TreeMaker make) { FlagsEnum.assertNoUnexpectedFlags(env.toplevel.packge.flags_field, - FlagsEnum.MASK_TYPE_FLAGS); + FlagsEnum.MASK_PACKAGE_FLAGS); ListBuffer translated = null; try { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index e8c36785f7614..a111b6404a5b5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1542,7 +1542,7 @@ public JavaFileObject writeClass(ClassSymbol c) throws IOException, PoolOverflow, StringOverflow { FlagsEnum.assertNoUnexpectedFlags(c.flags_field, - FlagsEnum.MASK_TYPE_FLAGS); + FlagsEnum.MASK_CLASS_FLAGS); String name = (c.owner.kind == MDL ? c.name : c.flatname).toString(); Location outLocn; diff --git a/test/langtools/tools/javac/flags/FlagsTest.java b/test/langtools/tools/javac/flags/FlagsTest.java index 766c56b41d128..a7a9051af331e 100644 --- a/test/langtools/tools/javac/flags/FlagsTest.java +++ b/test/langtools/tools/javac/flags/FlagsTest.java @@ -52,13 +52,11 @@ public static void main(String[] args) throws Throwable { private static void findFreeFlags() throws Throwable { Map>> target2Flag2Fields = computeTarget2Flag2Fields(); - long freeTypeFlags = ~collectFlags(target2Flag2Fields, FlagTarget.TYPE); - long freeMethodFlags = ~collectFlags(target2Flag2Fields, FlagTarget.METHOD); - long freeVariableFlags = ~collectFlags(target2Flag2Fields, FlagTarget.VARIABLE); + for (FlagTarget target : FlagTarget.values()) { + long freeFlags = ~collectFlags(target2Flag2Fields, target); - printFreeFlags("TYPE", freeTypeFlags); - printFreeFlags("METHOD", freeMethodFlags); - printFreeFlags("VARIABLE", freeVariableFlags); + printFreeFlags(target.name(), freeFlags); + } } private static Map>> computeTarget2Flag2Fields() throws Throwable { From e00209f0916e5c4d120fefe5de481ead4e67c227 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 25 Jul 2025 15:35:27 +0200 Subject: [PATCH 5/9] Fixing toString value for flags that have ACC_ overloaded flags. --- make/langtools/tools/flagsgenerator/FlagsGenerator.java | 7 +++++++ .../share/classes/com/sun/tools/javac/code/Flags.java | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/make/langtools/tools/flagsgenerator/FlagsGenerator.java b/make/langtools/tools/flagsgenerator/FlagsGenerator.java index a0ca8c08304e0..f3af883d19966 100644 --- a/make/langtools/tools/flagsgenerator/FlagsGenerator.java +++ b/make/langtools/tools/flagsgenerator/FlagsGenerator.java @@ -35,10 +35,12 @@ import java.util.ArrayList; import java.util.Arrays; 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; @@ -62,6 +64,7 @@ public static void main(String... args) throws IOException { Map> flag2Names = new TreeMap<>(); Map>> target2FlagBit2Fields = new HashMap<>(); Map customToString = new HashMap<>(); + Set noToString = new HashSet<>(); for (VariableElement field : ElementFilter.fieldsIn(clazz.getEnclosedElements())) { String flagName = field.getSimpleName().toString(); @@ -84,6 +87,9 @@ public static void main(String... args) throws IOException { 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); + } } } } @@ -115,6 +121,7 @@ public enum FlagsEnum { 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 + "\"),"); 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 a1f5b8121050e..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 @@ -124,12 +124,16 @@ public static EnumSet asFlagSet(long flags) { // files into unique bits positions: ACC_SYNTHETIC <-> SYNTHETIC, // for example. @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; /* *************************************** @@ -611,4 +615,8 @@ public enum FlagTarget { public @interface CustomToStringValue { public String value(); } + + @Retention(RetentionPolicy.RUNTIME) + public @interface NoToStringValue { + } } From f1d6def8f8572a1249901dc62194ca91e008222f Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 25 Jul 2025 18:56:10 +0200 Subject: [PATCH 6/9] Reverting runtime checks, as suggested. --- .../tools/flagsgenerator/FlagsGenerator.java | 29 ++----------------- .../com/sun/tools/javac/comp/Lower.java | 23 --------------- .../com/sun/tools/javac/jvm/ClassWriter.java | 9 ------ 3 files changed, 2 insertions(+), 59 deletions(-) diff --git a/make/langtools/tools/flagsgenerator/FlagsGenerator.java b/make/langtools/tools/flagsgenerator/FlagsGenerator.java index f3af883d19966..93d82fce1ef8b 100644 --- a/make/langtools/tools/flagsgenerator/FlagsGenerator.java +++ b/make/langtools/tools/flagsgenerator/FlagsGenerator.java @@ -33,7 +33,6 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -105,16 +104,10 @@ public static void main(String... args) throws IOException { } } - String masks = Arrays.stream(FlagTarget.values()) - .map(target -> " public static final long MASK_" + target.name() + "_FLAGS = " + getMask(target2FlagBit2Fields, target) + ";") - .collect(Collectors.joining("\n")); - try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(Paths.get(args[1])))) { out.println(""" package com.sun.tools.javac.code; - import com.sun.tools.javac.util.Assert; - public enum FlagsEnum { """); for (Entry> e : flag2Names.entrySet()) { @@ -129,8 +122,6 @@ public enum FlagsEnum { out.println(""" ; - ${masks} - private final long value; private final String toString; private FlagsEnum(long value, String toString) { @@ -143,28 +134,12 @@ public long value() { public String toString() { return toString; } - public static void assertNoUnexpectedFlags(long flags, long mask) { - Assert.check((flags & ~mask) == 0, - () -> "Unexpected flags: 0x" + Long.toHexString(flags & ~mask) + "L (" + - Flags.asFlagSet(flags & ~mask) + ")"); - } } - """.replace("${masks}", masks)); + """); } } } - private static String getMask(Map>> target2FlagBit2Fields, - FlagTarget target) { - long mask = target2FlagBit2Fields.get(target) - .keySet() - .stream() - .mapToLong(bit -> 1L << bit) - .reduce(0, (l, r) -> l | r); - - return "0x" + Long.toHexString(mask) + "L"; - } - private static Object valueOfValueAttribute(AnnotationMirror am) { return am.getElementValues() .values() @@ -173,7 +148,7 @@ private static Object valueOfValueAttribute(AnnotationMirror am) { .getValue(); } - public enum FlagTarget { + private enum FlagTarget { BLOCK, CLASS, METHOD, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index 2cabac7fc3c1f..aee7f0afe3975 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -2127,9 +2127,6 @@ private boolean needPackageInfoClass(JCPackageDecl pd) { } public void visitModuleDef(JCModuleDecl tree) { - FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, - FlagsEnum.MASK_MODULE_FLAGS); - ModuleSymbol msym = tree.sym; ClassSymbol c = msym.module_info; c.setAttributes(msym); @@ -2148,13 +2145,6 @@ private void createInfoClass(List annots, ClassSymbol c) { } public void visitClassDef(JCClassDecl tree) { - FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, - FlagsEnum.MASK_CLASS_FLAGS); - tree.sym - .getTypeParameters() - .forEach(tv -> FlagsEnum.assertNoUnexpectedFlags(tv.flags_field, - FlagsEnum.MASK_TYPE_VAR_FLAGS)); - Env prevEnv = attrEnv; ClassSymbol currentClassPrev = currentClass; MethodSymbol currentMethodSymPrev = currentMethodSym; @@ -2638,13 +2628,6 @@ JCFieldAccess makeIndyQualifier( } public void visitMethodDef(JCMethodDecl tree) { - FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, - FlagsEnum.MASK_METHOD_FLAGS); - tree.sym - .getTypeParameters() - .forEach(tv -> FlagsEnum.assertNoUnexpectedFlags(tv.flags_field, - FlagsEnum.MASK_TYPE_VAR_FLAGS)); - if (tree.name == names.init && (currentClass.flags_field&ENUM) != 0) { // Add "String $enum$name, int $enum$ordinal" to the beginning of the // argument list for each constructor of an enum. @@ -3732,9 +3715,6 @@ private void visitIterableForeachLoop(JCEnhancedForLoop tree) { } public void visitVarDef(JCVariableDecl tree) { - FlagsEnum.assertNoUnexpectedFlags(tree.sym.flags_field, - FlagsEnum.MASK_VARIABLE_FLAGS); - MethodSymbol oldMethodSym = currentMethodSym; int prevVariableIndex = variableIndex; tree.mods = translate(tree.mods); @@ -4369,9 +4349,6 @@ public void visitTry(JCTry tree) { * @param cdef The tree representing the class definition. */ public List translateTopLevelClass(Env env, JCTree cdef, TreeMaker make) { - FlagsEnum.assertNoUnexpectedFlags(env.toplevel.packge.flags_field, - FlagsEnum.MASK_PACKAGE_FLAGS); - ListBuffer translated = null; try { attrEnv = env; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index a111b6404a5b5..a24184e990b3d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -973,9 +973,6 @@ void writeBootstrapMethods() { /** Write field symbol, entering all references into constant pool. */ void writeField(VarSymbol v) { - FlagsEnum.assertNoUnexpectedFlags(v.flags_field, - FlagsEnum.MASK_VARIABLE_FLAGS); - int flags = adjustFlags(v.flags()); databuf.appendChar(flags); if (dumpFieldModifiers) { @@ -1001,9 +998,6 @@ void writeField(VarSymbol v) { /** Write method symbol, entering all references into constant pool. */ void writeMethod(MethodSymbol m) { - FlagsEnum.assertNoUnexpectedFlags(m.flags_field, - FlagsEnum.MASK_METHOD_FLAGS); - int flags = adjustFlags(m.flags()); databuf.appendChar(flags); if (dumpMethodModifiers) { @@ -1541,9 +1535,6 @@ void writeMethods(Scope s) { public JavaFileObject writeClass(ClassSymbol c) throws IOException, PoolOverflow, StringOverflow { - FlagsEnum.assertNoUnexpectedFlags(c.flags_field, - FlagsEnum.MASK_CLASS_FLAGS); - String name = (c.owner.kind == MDL ? c.name : c.flatname).toString(); Location outLocn; if (multiModuleMode) { From 019d0061355b35c8bd028aa22192e30502158704 Mon Sep 17 00:00:00 2001 From: Jan Lahoda <51319204+lahodaj@users.noreply.github.com> Date: Mon, 11 Aug 2025 11:38:06 +0200 Subject: [PATCH 7/9] Update make/modules/jdk.compiler/Gensrc.gmk as suggested. Co-authored-by: Magnus Ihse Bursie --- make/modules/jdk.compiler/Gensrc.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/modules/jdk.compiler/Gensrc.gmk b/make/modules/jdk.compiler/Gensrc.gmk index b5ec153256de7..14f649a888de4 100644 --- a/make/modules/jdk.compiler/Gensrc.gmk +++ b/make/modules/jdk.compiler/Gensrc.gmk @@ -79,7 +79,7 @@ TARGETS += $(PARSEPROPERTIES) ################################################################################ # -# Generate FlagsEnum from Flags constants: +# Generate FlagsEnum from Flags constants # TOOL_FLAGSGENERATOR_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \ From ab6486e42f36b378f6eaa459cfe987612a2acb64 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Mon, 11 Aug 2025 11:47:49 +0200 Subject: [PATCH 8/9] Removing unnecessary delimited, as suggested. --- make/modules/jdk.compiler/Gensrc.gmk | 2 -- 1 file changed, 2 deletions(-) diff --git a/make/modules/jdk.compiler/Gensrc.gmk b/make/modules/jdk.compiler/Gensrc.gmk index b5ec153256de7..6e7f28e6caed2 100644 --- a/make/modules/jdk.compiler/Gensrc.gmk +++ b/make/modules/jdk.compiler/Gensrc.gmk @@ -75,8 +75,6 @@ $(eval $(call SetupExecute, PARSEPROPERTIES, \ TARGETS += $(PARSEPROPERTIES) -################################################################################ - ################################################################################ # # Generate FlagsEnum from Flags constants: From 47ee2dbb2b363b729a9e4dc4847899804a51a1ca Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Mon, 11 Aug 2025 11:49:28 +0200 Subject: [PATCH 9/9] Using EnumMap, as suggested. --- make/langtools/tools/flagsgenerator/FlagsGenerator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/make/langtools/tools/flagsgenerator/FlagsGenerator.java b/make/langtools/tools/flagsgenerator/FlagsGenerator.java index 93d82fce1ef8b..1c192d214dee8 100644 --- a/make/langtools/tools/flagsgenerator/FlagsGenerator.java +++ b/make/langtools/tools/flagsgenerator/FlagsGenerator.java @@ -33,6 +33,7 @@ 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; @@ -61,7 +62,7 @@ public static void main(String... args) throws IOException { TypeElement clazz = (TypeElement) trees.getElement(new TreePath(new TreePath(cut), cut.getTypeDecls().get(0))); Map> flag2Names = new TreeMap<>(); - Map>> target2FlagBit2Fields = new HashMap<>(); + Map>> target2FlagBit2Fields = new EnumMap<>(FlagTarget.class); Map customToString = new HashMap<>(); Set noToString = new HashSet<>();