All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.parsing.parser.FeatureSet Maven / Gradle / Ivy

Go to download

Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs.

There is a newer version: v20240317
Show newest version
/*
 * Copyright 2015 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp.parsing.parser;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.annotations.InlineMe;
import java.io.Serializable;
import java.util.EnumSet;
import java.util.Set;

/**
 * Represents various aspects of language version and support.
 *
 * 

This is somewhat redundant with LanguageMode, but is separate for two reasons: (1) it's used * for parsing, which cannot depend on LanguageMode, and (2) it's concerned with slightly different * nuances: implemented features and modules rather than strictness. * *

In the long term, it would be good to disentangle all these concerns and pull out a single * LanguageSyntax enum with a separate strict mode flag, and then these could possibly be unified. * *

Instances of this class are immutable. */ @Immutable public final class FeatureSet implements Serializable { private final ImmutableSet features; /** The bare minimum set of features. */ public static final FeatureSet BARE_MINIMUM = new FeatureSet(emptyEnumSet()); /** Features from ES3. */ public static final FeatureSet ES3 = BARE_MINIMUM.with(LangVersion.ES3.features()); /** Features from ES5 only. */ public static final FeatureSet ES5 = ES3.with(LangVersion.ES5.features()); /** All ES2015 features, including modules. */ public static final FeatureSet ES2015_MODULES = ES5.with(LangVersion.ES2015.features()); /** The full set of ES2015 features, not including modules. */ public static final FeatureSet ES2015 = ES2015_MODULES.without(Feature.MODULES); public static final FeatureSet ES2016_MODULES = ES2015_MODULES.with(LangVersion.ES2016.features()); public static final FeatureSet ES2016 = ES2016_MODULES.without(Feature.MODULES); public static final FeatureSet ES2017_MODULES = ES2016_MODULES.with(LangVersion.ES2017.features()); public static final FeatureSet ES2017 = ES2017_MODULES.without(Feature.MODULES); public static final FeatureSet ES2018_MODULES = ES2017_MODULES.with(LangVersion.ES2018.features()); public static final FeatureSet ES2018 = ES2018_MODULES.without(Feature.MODULES); public static final FeatureSet ES2019_MODULES = ES2018_MODULES.with(LangVersion.ES2019.features()); public static final FeatureSet ES2019 = ES2019_MODULES.without(Feature.MODULES); public static final FeatureSet ES2020_MODULES = ES2019_MODULES.with(LangVersion.ES2020.features()); public static final FeatureSet ES2020 = ES2020_MODULES.without(Feature.MODULES); public static final FeatureSet ES2021_MODULES = ES2020_MODULES.with(LangVersion.ES2021.features()); public static final FeatureSet ES2021 = ES2021_MODULES.without(Feature.MODULES); // Set of all fully supported features, even those part of language versions not fully supported public static final FeatureSet ES_NEXT = ES2021_MODULES.with(LangVersion.ES_NEXT.features()); // Set of features fully supported in checks, even those not fully supported in optimizations public static final FeatureSet ES_UNSTABLE = ES_NEXT.with(LangVersion.ES_UNSTABLE.features()); // Set of all features that can be parsed, even those not yet fully supported in checks. public static final FeatureSet ES_UNSUPPORTED = ES_UNSTABLE.with(LangVersion.ES_UNSUPPORTED.features()); public static final FeatureSet BROWSER_2020 = ES2019_MODULES.without( // https://kangax.github.io/compat-table/es2016plus/ // All four of these are missing in Firefox 71 and lookbehind is missing in Safari 13. Feature.REGEXP_FLAG_S, Feature.REGEXP_LOOKBEHIND, Feature.REGEXP_NAMED_GROUPS, Feature.REGEXP_UNICODE_PROPERTY_ESCAPE); public static final FeatureSet BROWSER_2021 = ES2020_MODULES.without( // https://kangax.github.io/compat-table/es2016plus/ // Regexp lookbehind is missing in Safari 14. Feature.REGEXP_LOOKBEHIND); public static final FeatureSet BROWSER_2022 = ES2021_MODULES.without( // https://kangax.github.io/compat-table/es2016plus/ // Regexp lookbehind is still missing in Safari 15. Feature.REGEXP_LOOKBEHIND); public static final FeatureSet BROWSER_2023 = ES2021_MODULES.without( // https://kangax.github.io/compat-table/es2016plus/ // Regexp lookbehind is still missing in Safari 16.2! It's in Safari TP though so // 2024 shouldn't need this awkward hack. We can't bump up to ES2022 here because // Safari 16.2 doesn't support class static blocks. // IMPORTANT: There is special casing for this feature and the ones excluded for // BROWSER_2020 above in RewritePolyfills. // If future Browser FeatureSet Year definitions have to remove any other features, then // we need to change the way that is done to avoid incorrect inclusion of polyfills. Feature.REGEXP_LOOKBEHIND); public static final FeatureSet ALL = ES_UNSUPPORTED.with(LangVersion.TYPESCRIPT.features()); private enum LangVersion { ES3, ES5, ES2015, ES2016, ES2017, ES2018, ES2019, ES2020, ES2021, ES_NEXT, ES_UNSTABLE, ES_UNSUPPORTED, TYPESCRIPT, ; private EnumSet features() { EnumSet set = EnumSet.noneOf(Feature.class); for (Feature feature : Feature.values()) { if (feature.version == this) { set.add(feature); } } return set; } } /** Specific features that can be included in a FeatureSet. */ public enum Feature { // ES5 features ES3_KEYWORDS_AS_IDENTIFIERS("ES3 keywords as identifiers", LangVersion.ES5), GETTER("getters", LangVersion.ES5), KEYWORDS_AS_PROPERTIES("reserved words as properties", LangVersion.ES5), SETTER("setters", LangVersion.ES5), STRING_CONTINUATION("string continuation", LangVersion.ES5), TRAILING_COMMA("trailing comma", LangVersion.ES5), // ES2015 features (besides modules): all stable browsers are now fully compliant ARRAY_PATTERN_REST("array pattern rest", LangVersion.ES2015), ARROW_FUNCTIONS("arrow function", LangVersion.ES2015), BINARY_LITERALS("binary literal", LangVersion.ES2015), BLOCK_SCOPED_FUNCTION_DECLARATION("block-scoped function declaration", LangVersion.ES2015), CLASSES("class", LangVersion.ES2015), CLASS_EXTENDS("class extends", LangVersion.ES2015), CLASS_GETTER_SETTER("class getters/setters", LangVersion.ES2015), COMPUTED_PROPERTIES("computed property", LangVersion.ES2015), CONST_DECLARATIONS("const declaration", LangVersion.ES2015), DEFAULT_PARAMETERS("default parameter", LangVersion.ES2015), ARRAY_DESTRUCTURING("array destructuring", LangVersion.ES2015), OBJECT_DESTRUCTURING("object destructuring", LangVersion.ES2015), EXTENDED_OBJECT_LITERALS("extended object literal", LangVersion.ES2015), FOR_OF("for-of loop", LangVersion.ES2015), GENERATORS("generator", LangVersion.ES2015), LET_DECLARATIONS("let declaration", LangVersion.ES2015), MEMBER_DECLARATIONS("member declaration", LangVersion.ES2015), NEW_TARGET("new.target", LangVersion.ES2015), OCTAL_LITERALS("octal literal", LangVersion.ES2015), REGEXP_FLAG_U("RegExp flag 'u'", LangVersion.ES2015), REGEXP_FLAG_Y("RegExp flag 'y'", LangVersion.ES2015), REST_PARAMETERS("rest parameter", LangVersion.ES2015), SPREAD_EXPRESSIONS("spread expression", LangVersion.ES2015), SUPER("super", LangVersion.ES2015), TEMPLATE_LITERALS("template literal", LangVersion.ES2015), // ES modules MODULES("modules", LangVersion.ES2015), // ES 2016 only added one new feature: EXPONENT_OP("exponent operator (**)", LangVersion.ES2016), // ES 2017 features: ASYNC_FUNCTIONS("async function", LangVersion.ES2017), TRAILING_COMMA_IN_PARAM_LIST("trailing comma in param list", LangVersion.ES2017), // ES 2018 adds https://github.com/tc39/proposal-object-rest-spread OBJECT_LITERALS_WITH_SPREAD("object literals with spread", LangVersion.ES2018), OBJECT_PATTERN_REST("object pattern rest", LangVersion.ES2018), // https://github.com/tc39/proposal-async-iteration ASYNC_GENERATORS("async generator functions", LangVersion.ES2018), FOR_AWAIT_OF("for-await-of loop", LangVersion.ES2018), // ES 2018 adds Regex Features: // https://github.com/tc39/proposal-regexp-dotall-flag REGEXP_FLAG_S("RegExp flag 's'", LangVersion.ES2018), // https://github.com/tc39/proposal-regexp-lookbehind REGEXP_LOOKBEHIND("RegExp Lookbehind", LangVersion.ES2018), // https://github.com/tc39/proposal-regexp-named-groups REGEXP_NAMED_GROUPS("RegExp named groups", LangVersion.ES2018), // https://github.com/tc39/proposal-regexp-unicode-property-escapes REGEXP_UNICODE_PROPERTY_ESCAPE("RegExp unicode property escape", LangVersion.ES2018), // ES 2019 adds https://github.com/tc39/proposal-json-superset UNESCAPED_UNICODE_LINE_OR_PARAGRAPH_SEP( "Unescaped unicode line or paragraph separator", LangVersion.ES2019), // ES 2019 adds optional catch bindings: // https://github.com/tc39/proposal-optional-catch-binding OPTIONAL_CATCH_BINDING("Optional catch binding", LangVersion.ES2019), // ES 2020 Stage 4 DYNAMIC_IMPORT("Dynamic module import", LangVersion.ES2020), BIGINT("bigint", LangVersion.ES2020), IMPORT_META("import.meta", LangVersion.ES2020), NULL_COALESCE_OP("Nullish coalescing", LangVersion.ES2020), OPTIONAL_CHAINING("Optional chaining", LangVersion.ES2020), // ES 2021 Stage 4 NUMERIC_SEPARATOR("numeric separator", LangVersion.ES2021), LOGICAL_ASSIGNMENT("Logical assignments", LangVersion.ES2021), // ES 2022 adds https://github.com/tc39/proposal-regexp-match-indices REGEXP_FLAG_D("RegExp flag 'd'", LangVersion.ES_NEXT), // ES_NEXT: Features that are fully supported, but part of a language version that is not yet // fully supported // ES_UNSTABLE: Features fully supported in checks, but not fully supported everywhere else PUBLIC_CLASS_FIELDS("Public class fields", LangVersion.ES_UNSTABLE), // Part of ES2022 // ES 2022 adds https://github.com/tc39/proposal-class-static-block CLASS_STATIC_BLOCK("Class static block", LangVersion.ES_UNSTABLE), // ES_UNSUPPORTED: Features that we can parse, but not yet supported in all checks // TypeScript type syntax that will never be implemented in browsers. Only used as an indicator // to the CodeGenerator that it should handle type syntax. TYPE_ANNOTATION("type annotation", LangVersion.TYPESCRIPT); private final String name; private final LangVersion version; private Feature(String name, LangVersion version) { this.name = name; this.version = version; } @Override public String toString() { return name; } } private FeatureSet(EnumSet features) { // ImmutableSet will only use an EnumSet if the set starts as an EnumSet. this.features = ImmutableSet.copyOf(features); } /** * Returns a string representation suitable for use in polyfill definition files and encoding in * depgraph and deps.js files. */ public String version() { if (ES3.contains(this)) { return "es3"; } if (ES5.contains(this)) { return "es5"; } if (ES2015_MODULES.contains(this)) { return "es6"; } if (ES2016_MODULES.contains(this)) { return "es7"; } if (ES2017_MODULES.contains(this)) { return "es8"; } if (ES2018_MODULES.contains(this)) { return "es9"; } if (ES2019_MODULES.contains(this)) { return "es_2019"; } if (ES2020_MODULES.contains(this)) { return "es_2020"; } if (ES2021_MODULES.contains(this)) { return "es_2021"; } if (ES_NEXT.contains(this)) { return "es_next"; } if (ES_UNSTABLE.contains(this)) { return "es_unstable"; } if (ES_UNSUPPORTED.contains(this)) { return "es_unsupported"; } if (ALL.contains(this)) { return "all"; } throw new IllegalStateException(this.toString()); } /** * Returns a string representation useful for debugging. * * @deprecated Please use {@link #version()} instead. */ @Deprecated @InlineMe(replacement = "this.version()") public String versionForDebugging() { return version(); } public FeatureSet without(Feature featureToRemove, Feature... moreFeaturesToRemove) { return new FeatureSet(difference(features, EnumSet.of(featureToRemove, moreFeaturesToRemove))); } public FeatureSet without(FeatureSet other) { return new FeatureSet(difference(features, other.features)); } public FeatureSet withoutTypes() { return new FeatureSet(difference(features, LangVersion.TYPESCRIPT.features())); } /** * Returns a new {@link FeatureSet} including all features of both {@code this} and {@code other}. */ public FeatureSet union(FeatureSet other) { return new FeatureSet(union(features, other.features)); } /** Does this {@link FeatureSet} contain at least one of the features of {@code other}? */ public boolean containsAtLeastOneOf(FeatureSet other) { for (Feature otherFeature : other.features) { if (this.features.contains(otherFeature)) { return true; } } return false; } /** Does this {@link FeatureSet} contain all of the features of {@code other}? */ public boolean contains(FeatureSet other) { return this.features.containsAll(other.features); } /** Does this {@link FeatureSet} contain the given feature? */ public boolean contains(Feature feature) { return this.features.containsAll(EnumSet.of(feature)); } private static EnumSet emptyEnumSet() { return EnumSet.noneOf(Feature.class); } private static EnumSet enumSetOf(Set set) { return set.isEmpty() ? emptyEnumSet() : EnumSet.copyOf(set); } private static EnumSet add(Set features, Feature feature) { EnumSet result = enumSetOf(features); result.add(feature); return result; } private static EnumSet union(Set features, Set newFeatures) { EnumSet result = enumSetOf(features); result.addAll(newFeatures); return result; } private static EnumSet difference(Set features, Set removedFeatures) { EnumSet result = enumSetOf(features); result.removeAll(removedFeatures); return result; } /** Returns a feature set combining all the features from {@code this} and {@code feature}. */ public FeatureSet with(Feature feature) { if (features.contains(feature)) { return this; } return new FeatureSet(add(features, feature)); } /** Returns a feature set combining all the features from {@code this} and {@code newFeatures}. */ @VisibleForTesting public FeatureSet with(Feature... newFeatures) { return new FeatureSet(union(features, ImmutableSet.copyOf(newFeatures))); } /** Returns a feature set combining all the features from {@code this} and {@code newFeatures}. */ @VisibleForTesting public FeatureSet with(Set newFeatures) { return new FeatureSet(union(features, newFeatures)); } /** Returns a feature set combining all the features from {@code this} and {@code newFeatures}. */ @VisibleForTesting public FeatureSet with(FeatureSet newFeatures) { return new FeatureSet(union(features, newFeatures.features)); } /** Does this {@link FeatureSet} include {@code feature}? */ public boolean has(Feature feature) { return features.contains(feature); } public ImmutableSet getFeatures() { return features; } @Override public boolean equals(Object other) { return other instanceof FeatureSet && ((FeatureSet) other).features.equals(features); } @Override public int hashCode() { return features.hashCode(); } @Override public String toString() { return features.toString(); } /** Parses known strings into feature sets. */ public static FeatureSet valueOf(String name) { switch (name) { case "es3": return ES3; case "es5": return ES5; case "es_2015": case "es6": return ES2015; case "es_2016": case "es7": return ES2016; case "es_2017": case "es8": return ES2017; case "es_2018": case "es9": return ES2018; case "es_2019": return ES2019; case "es_2020": return ES2020; case "es_2021": return ES2021; case "es_next": return ES_NEXT; case "es_unstable": return ES_UNSTABLE; case "es_unsupported": return ES_UNSUPPORTED; case "all": return ALL; default: throw new IllegalArgumentException("No such FeatureSet: " + name); } } /** * Returns a {@code FeatureSet} containing all known features. * *

NOTE: {@code PassFactory} classes that claim to support {@code FeatureSet.all()} should be * only those that cannot be broken by new features being added to the language. Mainly these are * passes that don't have to actually look at the AST at all, like empty marker passes. */ public static FeatureSet all() { return ALL; } public static FeatureSet latest() { return ES_UNSUPPORTED; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy