1 /* 2 * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javac.code; 27 28 import com.sun.tools.javac.code.Lint.LintCategory; 29 import com.sun.tools.javac.code.Source.Feature; 30 import com.sun.tools.javac.code.Symbol.ModuleSymbol; 31 import com.sun.tools.javac.jvm.Target; 32 import com.sun.tools.javac.resources.CompilerProperties.Errors; 33 import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; 34 import com.sun.tools.javac.resources.CompilerProperties.Warnings; 35 import com.sun.tools.javac.util.Assert; 36 import com.sun.tools.javac.util.Context; 37 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 38 import com.sun.tools.javac.util.JCDiagnostic.Error; 39 import com.sun.tools.javac.util.JCDiagnostic.LintWarning; 40 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; 41 import com.sun.tools.javac.util.JCDiagnostic.Warning; 42 import com.sun.tools.javac.util.Log; 43 import com.sun.tools.javac.util.Names; 44 import com.sun.tools.javac.util.Options; 45 46 import javax.tools.JavaFileObject; 47 import java.util.HashMap; 48 import java.util.HashSet; 49 import java.util.Map; 50 import java.util.Set; 51 52 import static com.sun.tools.javac.main.Option.PREVIEW; 53 import com.sun.tools.javac.util.JCDiagnostic; 54 55 /** 56 * Helper class to handle preview language features. This class maps certain language features 57 * (see {@link Feature} into 'preview' features; the mapping is completely ad-hoc, so as to allow 58 * for maximum flexibility, which allows to migrate preview feature into supported features with ease. 59 * 60 * This class acts as a centralized point against which usages of preview features are reported by 61 * clients (e.g. other javac classes). Internally, this class collects all such usages and generates 62 * diagnostics to inform the user of such usages. Such diagnostics can be enabled using the 63 * {@link LintCategory#PREVIEW} lint category, and are suppressible by usual means. 64 */ 65 public class Preview { 66 67 /** flag: are preview features enabled */ 68 private final boolean enabled; 69 70 /** test flag: should all features be considered as preview features? */ 71 private final boolean forcePreview; 72 73 /** a mapping from classfile numbers to Java SE versions */ 74 private final Map<Integer, Source> majorVersionToSource; 75 76 private final Set<JavaFileObject> sourcesWithPreviewFeatures = new HashSet<>(); 77 78 private final Names names; 79 private final Log log; 80 private final Source source; 81 82 protected static final Context.Key<Preview> previewKey = new Context.Key<>(); 83 84 public static Preview instance(Context context) { 85 Preview instance = context.get(previewKey); 86 if (instance == null) { 87 instance = new Preview(context); 88 } 89 return instance; 90 } 91 92 @SuppressWarnings("this-escape") 93 protected Preview(Context context) { 94 context.put(previewKey, this); 95 Options options = Options.instance(context); 96 names = Names.instance(context); 97 enabled = options.isSet(PREVIEW); 98 log = Log.instance(context); 99 source = Source.instance(context); 100 forcePreview = options.isSet("forcePreview"); 101 majorVersionToSource = initMajorVersionToSourceMap(); 102 } 103 104 private Map<Integer, Source> initMajorVersionToSourceMap() { 105 Map<Integer, Source> majorVersionToSource = new HashMap<>(); 106 for (Target t : Target.values()) { 107 int major = t.majorVersion; 108 Source source = Source.lookup(t.name); 109 if (source != null) { 110 majorVersionToSource.put(major, source); 111 } 112 } 113 return majorVersionToSource; 114 } 115 116 /** 117 * Returns true if {@code s} is deemed to participate in the preview of {@code previewSymbol}, and 118 * therefore no warnings or errors will be produced. 119 * 120 * @param syms the symbol table 121 * @param s the symbol depending on the preview symbol 122 * @param previewSymbol the preview symbol marked with @Preview 123 * @return true if {@code s} is participating in the preview of {@code previewSymbol} 124 */ 125 public boolean participatesInPreview(Symtab syms, Symbol s, Symbol previewSymbol) { 126 // All symbols in the same module as the preview symbol participate in the preview API 127 if (previewSymbol.packge().modle == s.packge().modle) { 128 return true; 129 } 130 131 return participatesInPreview(syms, s.packge().modle); 132 } 133 134 /** 135 * Returns true if module {@code m} is deemed to participate in the preview, and 136 * therefore no warnings or errors will be produced. 137 * 138 * @param syms the symbol table 139 * @param m the module to check 140 * @return true if {@code m} is participating in the preview of {@code previewSymbol} 141 */ 142 public boolean participatesInPreview(Symtab syms, ModuleSymbol m) { 143 // If java.base's jdk.internal.javac package is exported to s's module then 144 // s participates in the preview API 145 return syms.java_base.exports.stream() 146 .filter(ed -> ed.packge.fullname == names.jdk_internal_javac) 147 .anyMatch(ed -> ed.modules.contains(m)); 148 } 149 150 /** 151 * Report usage of a preview feature. Usages reported through this method will affect the 152 * set of sourcefiles with dependencies on preview features. 153 * @param pos the position at which the preview feature was used. 154 * @param feature the preview feature used. 155 */ 156 public void warnPreview(int pos, Feature feature) { 157 warnPreview(new SimpleDiagnosticPosition(pos), feature); 158 } 159 160 /** 161 * Report usage of a preview feature. Usages reported through this method will affect the 162 * set of sourcefiles with dependencies on preview features. 163 * @param pos the position at which the preview feature was used. 164 * @param feature the preview feature used. 165 */ 166 public void warnPreview(DiagnosticPosition pos, Feature feature) { 167 Assert.check(isEnabled()); 168 Assert.check(isPreview(feature)); 169 markUsesPreview(pos); 170 log.warning(pos, 171 feature.isPlural() ? 172 LintWarnings.PreviewFeatureUsePlural(feature.nameFragment()) : 173 LintWarnings.PreviewFeatureUse(feature.nameFragment())); 174 } 175 176 /** 177 * Report usage of a preview feature in classfile. 178 * @param classfile the name of the classfile with preview features enabled 179 * @param majorVersion the major version found in the classfile. 180 */ 181 public void warnPreview(JavaFileObject classfile, int majorVersion) { 182 Assert.check(isEnabled()); 183 log.warning(LintWarnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name)); 184 } 185 186 /** 187 * Mark the current source file as using a preview feature. The corresponding classfile 188 * will be generated with minor version {@link ClassFile#PREVIEW_MINOR_VERSION}. 189 * @param pos the position at which the preview feature was used. 190 */ 191 public void markUsesPreview(DiagnosticPosition pos) { 192 sourcesWithPreviewFeatures.add(log.currentSourceFile()); 193 } 194 195 public boolean usesPreview(JavaFileObject file) { 196 return sourcesWithPreviewFeatures.contains(file); 197 } 198 199 /** 200 * Are preview features enabled? 201 * @return true, if preview features are enabled. 202 */ 203 public boolean isEnabled() { 204 return enabled; 205 } 206 207 /** 208 * Is given feature a preview feature? 209 * @param feature the feature to be tested. 210 * @return true, if given feature is a preview feature. 211 */ 212 public boolean isPreview(Feature feature) { 213 return switch (feature) { 214 case PRIMITIVE_PATTERNS -> true; 215 case VALUE_CLASSES -> true; 216 //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). 217 //When real preview features will be added, this method can be implemented to return 'true' 218 //for those selected features, and 'false' for all the others. 219 default -> forcePreview; 220 }; 221 } 222 223 /** 224 * Generate an error key which captures the fact that a given preview feature could not be used 225 * due to the preview feature support being disabled. 226 * @param feature the feature for which the diagnostic has to be generated. 227 * @return the diagnostic. 228 */ 229 public Error disabledError(Feature feature) { 230 Assert.check(!isEnabled()); 231 return feature.isPlural() ? 232 Errors.PreviewFeatureDisabledPlural(feature.nameFragment()) : 233 Errors.PreviewFeatureDisabled(feature.nameFragment()); 234 } 235 236 /** 237 * Generate an error key which captures the fact that a preview classfile cannot be loaded 238 * due to the preview feature support being disabled. 239 * @param classfile the name of the classfile with preview features enabled 240 * @param majorVersion the major version found in the classfile. 241 */ 242 public Error disabledError(JavaFileObject classfile, int majorVersion) { 243 Assert.check(!isEnabled()); 244 return Errors.PreviewFeatureDisabledClassfile(classfile, majorVersionToSource.get(majorVersion).name); 245 } 246 247 /** 248 * Check whether the given symbol has been declared using 249 * a preview language feature. 250 * 251 * @param sym Symbol to check 252 * @return true iff sym has been declared using a preview language feature 253 */ 254 public boolean declaredUsingPreviewFeature(Symbol sym) { 255 return false; 256 } 257 258 public void checkSourceLevel(DiagnosticPosition pos, Feature feature) { 259 if (isPreview(feature) && !isEnabled()) { 260 //preview feature without --preview flag, error 261 log.error(pos, disabledError(feature)); 262 } else { 263 if (!feature.allowedInSource(source)) { 264 log.error(pos, feature.error(source.name)); 265 } 266 if (isEnabled() && isPreview(feature)) { 267 warnPreview(pos, feature); 268 } 269 } 270 } 271 272 }