Skip to content

Commit

Permalink
Change the programmatic interface to compiler options to no longer ha…
Browse files Browse the repository at this point in the history
…ve a language out but an output feature set. This allows anything linking against the compiler to be much more specific in what it wants out (e.g. "Please give me ES2017 but without modules").

Note that for the CLI there is still only a language out option. But the language out represents a feature set.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=198058737
  • Loading branch information
johnplaisted authored and brad4d committed May 25, 2018
1 parent e2da376 commit 524c5fd
Show file tree
Hide file tree
Showing 13 changed files with 60 additions and 42 deletions.
Expand Up @@ -114,7 +114,7 @@ boolean nodeTypeMayHaveSideEffects(Node n) {
*/
boolean isEcmaScript5OrGreater() {
return compiler != null
&& compiler.getOptions().getLanguageOut().toFeatureSet().contains(FeatureSet.ES5);
&& compiler.getOptions().getOutputFeatureSet().contains(FeatureSet.ES5);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/com/google/javascript/jscomp/CoalesceVariableNames.java
Expand Up @@ -32,6 +32,7 @@
import com.google.javascript.jscomp.graph.GraphNode;
import com.google.javascript.jscomp.graph.LinkedUndirectedGraph;
import com.google.javascript.jscomp.graph.UndiGraph;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
Expand Down Expand Up @@ -137,7 +138,7 @@ public void enterScope(NodeTraversal t) {
new LiveVariablesAnalysis(
cfg, scope, null, compiler, new Es6SyntacticScopeCreator(compiler));

if (compiler.getOptions().getLanguageOut() == CompilerOptions.LanguageMode.ECMASCRIPT3) {
if (FeatureSet.ES3.contains(compiler.getOptions().getOutputFeatureSet())) {
// If the function has exactly 2 params, mark them as escaped. This is a work-around for a
// bug in IE 8 and below, where it throws an exception if you write to the parameters of the
// callback in a sort(). See http://blickly.github.io/closure-compiler-issues/#58 and
Expand Down
4 changes: 2 additions & 2 deletions src/com/google/javascript/jscomp/CodePrinter.java
Expand Up @@ -23,7 +23,7 @@
import com.google.common.collect.ImmutableList;
import com.google.debugging.sourcemap.FilePosition;
import com.google.javascript.jscomp.CodePrinter.Builder.CodeGeneratorFactory;
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.StaticSourceFile;
import com.google.javascript.rhino.Token;
Expand Down Expand Up @@ -837,7 +837,7 @@ static Format fromOptions(CompilerOptions options, boolean outputTypes, boolean
if (outputTypes) {
return Format.TYPED;
}
if (prettyPrint || options.getLanguageOut() == LanguageMode.ECMASCRIPT6_TYPED) {
if (prettyPrint || options.getOutputFeatureSet().contains(FeatureSet.TYPESCRIPT)) {
return Format.PRETTY;
}
return Format.COMPACT;
Expand Down
61 changes: 41 additions & 20 deletions src/com/google/javascript/jscomp/CompilerOptions.java
Expand Up @@ -113,9 +113,11 @@ public enum PropertyCollapseLevel {
private LanguageMode languageIn;

/**
* The JavaScript language version that should be produced.
* The JavaScript features that are allowed to be in the output.
*/
private LanguageMode languageOut;
private FeatureSet outputFeatureSet;

private boolean languageOutIsDefaultStrict;

/**
* The builtin set of externs to be used
Expand Down Expand Up @@ -1216,7 +1218,8 @@ public void setPrintConfig(boolean printConfig) {
public CompilerOptions() {
// Accepted language
languageIn = LanguageMode.ECMASCRIPT_2017;
languageOut = LanguageMode.NO_TRANSPILE;
outputFeatureSet = LanguageMode.NO_TRANSPILE.toFeatureSet();
languageOutIsDefaultStrict = false;

// Which environment to use
environment = Environment.BROWSER;
Expand Down Expand Up @@ -1913,7 +1916,7 @@ public TweakProcessing getTweakProcessing() {
public void setLanguage(LanguageMode language) {
checkState(language != LanguageMode.NO_TRANSPILE);
this.languageIn = language;
this.languageOut = language;
this.setLanguageOut(language);
}

/**
Expand All @@ -1930,28 +1933,43 @@ public LanguageMode getLanguageIn() {
}

/**
* Sets ECMAScript version to use for the output. If you are not
* transpiling from one version to another, use #setLanguage instead.
* Sets ECMAScript version to use for the output.
*
* <p>If you are not transpiling from one version to another, use #setLanguage instead.
*
* <p>If you you need something more fine grained (e.g. "ES2017 without modules") use
* #setOutputFeatureSet.
*/
public void setLanguageOut(LanguageMode languageOut) {
this.languageOut = languageOut;
this.languageOutIsDefaultStrict = languageOut.isDefaultStrict();
this.outputFeatureSet = languageOut.toFeatureSet();
}

public LanguageMode getLanguageOut() {
if (languageOut == LanguageMode.NO_TRANSPILE) {
return languageIn;
}
return languageOut;
/**
* Sets the features that allowed to appear in the output. Any feature in the input that is not
* in this output must be transpiled away.
*/
public void setOutputFeatureSet(FeatureSet featureSet) {
this.outputFeatureSet = featureSet;
}

/**
* Gets the set of features that can appear in the output.
*/
public FeatureSet getOutputFeatureSet() {
return outputFeatureSet;
}

public boolean needsTranspilationFrom(FeatureSet languageLevel) {
// TODO(johnplaisted): This isn't really accurate. This should instead be the *parsed* language,
// not the *input* language.
return getLanguageIn().toFeatureSet().contains(languageLevel)
&& !getLanguageOut().toFeatureSet().contains(languageLevel);
&& !getOutputFeatureSet().contains(languageLevel);
}

public boolean needsTranspilationOf(FeatureSet.Feature feature) {
return getLanguageIn().toFeatureSet().has(feature)
&& !getLanguageOut().toFeatureSet().has(feature);
&& !getOutputFeatureSet().has(feature);
}

/**
Expand Down Expand Up @@ -2632,7 +2650,7 @@ public boolean shouldQuoteKeywordProperties() {
if (incrementalCheckMode == IncrementalCheckMode.GENERATE_IJS) {
return false;
}
return this.quoteKeywordProperties || languageOut == LanguageMode.ECMASCRIPT3;
return this.quoteKeywordProperties || FeatureSet.ES3.contains(getOutputFeatureSet());
}

public void setErrorFormat(ErrorFormat errorFormat) {
Expand Down Expand Up @@ -2802,8 +2820,11 @@ public void setConformanceConfigs(List<ConformanceConfig> configs) {
.build();
}

/**
* Whether the output should contain a 'use strict' directive.
*/
public boolean shouldEmitUseStrict() {
return this.emitUseStrict.or(getLanguageOut().isDefaultStrict());
return this.emitUseStrict.or(languageOutIsDefaultStrict);
}

public CompilerOptions setEmitUseStrict(boolean emitUseStrict) {
Expand Down Expand Up @@ -2956,7 +2977,7 @@ public String toString() {
.add("j2clPassMode", j2clPassMode)
.add("labelRenaming", labelRenaming)
.add("languageIn", getLanguageIn())
.add("languageOut", getLanguageOut())
.add("languageOutIsDefaultStrict", languageOutIsDefaultStrict)
.add("legacyCodeCompile", legacyCodeCompile)
.add("lineBreak", lineBreak)
.add("lineLengthThreshold", lineLengthThreshold)
Expand All @@ -2972,6 +2993,7 @@ public String toString() {
.add("optimizeArgumentsArray", optimizeArgumentsArray)
.add("optimizeCalls", optimizeCalls)
.add("outputCharset", outputCharset)
.add("outputFeatureSet", outputFeatureSet)
.add("outputJs", outputJs)
.add("outputJsStringUsage", outputJsStringUsage)
.add(
Expand Down Expand Up @@ -3066,7 +3088,7 @@ public String toString() {
* the same compilation job. Therefore, the 'use strict' directive is ignored
* when the language mode is not strict.
*/
public static enum LanguageMode {
public enum LanguageMode {
/**
* 90's JavaScript
*/
Expand Down Expand Up @@ -3157,11 +3179,10 @@ public FeatureSet toFeatureSet() {
case ECMASCRIPT_2018:
return FeatureSet.ES2018_MODULES;
case ECMASCRIPT_NEXT:
case NO_TRANSPILE:
return FeatureSet.ES_NEXT;
case ECMASCRIPT6_TYPED:
return FeatureSet.TYPESCRIPT;
case NO_TRANSPILE:
throw new IllegalStateException();
}
throw new IllegalStateException();
}
Expand Down
Expand Up @@ -56,7 +56,7 @@ static void preprocess(CompilerOptions options) {
}

if (options.dartPass) {
if (!options.getLanguageOut().toFeatureSet().contains(FeatureSet.ES5)) {
if (!options.getOutputFeatureSet().contains(FeatureSet.ES5)) {
throw new InvalidOptionsException("Dart requires --language_out=ES5 or higher.");
}
// --dart_pass does not support type-aware property renaming yet.
Expand Down
Expand Up @@ -54,7 +54,7 @@ public DartSuperAccessorsPass(AbstractCompiler compiler) {
this.renameProperties = options.propertyRenaming == PropertyRenamingPolicy.ALL_UNQUOTED;

checkState(
options.getLanguageOut().toFeatureSet().contains(FeatureSet.ES5),
options.getOutputFeatureSet().contains(FeatureSet.ES5),
"Dart super accessors pass requires ES5+ output");

// We currently rely on JSCompiler_renameProperty, which is not type-aware.
Expand Down
6 changes: 3 additions & 3 deletions src/com/google/javascript/jscomp/DefaultPassConfig.java
Expand Up @@ -371,7 +371,7 @@ protected List<PassFactory> getChecks() {

// It's important that the Dart super accessors pass run *before* es6ConvertSuper,
// which is a "late" ES6 pass. This is enforced in the assertValidOrder method.
if (options.dartPass && !options.getLanguageOut().toFeatureSet().contains(ES6)) {
if (options.dartPass && !options.getOutputFeatureSet().contains(ES6)) {
checks.add(dartSuperAccessorsPass);
}

Expand Down Expand Up @@ -894,7 +894,7 @@ protected List<PassFactory> getOptimizations() {
passes.add(varCheckValidity);

// Raise to ES6, if allowed
if (options.getLanguageOut().toFeatureSet().contains(ES6)) {
if (options.getOutputFeatureSet().contains(ES6)) {
passes.add(optimizeToEs6);
}

Expand Down Expand Up @@ -2374,7 +2374,7 @@ protected CompilerPass create(final AbstractCompiler compiler) {
return new ClosureOptimizePrimitives(
compiler,
compiler.getOptions().propertyRenaming == PropertyRenamingPolicy.ALL_UNQUOTED,
compiler.getOptions().getLanguageOut().toFeatureSet().contains(ES6));
compiler.getOptions().getOutputFeatureSet().contains(ES6));
}

@Override
Expand Down
Expand Up @@ -15,7 +15,6 @@
*/
package com.google.javascript.jscomp;

import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.jscomp.NodeTraversal.Callback;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.rhino.IR;
Expand Down Expand Up @@ -65,7 +64,7 @@ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
break;
case GETTER_DEF:
case SETTER_DEF:
if (compiler.getOptions().getLanguageOut() == LanguageMode.ECMASCRIPT3) {
if (FeatureSet.ES3.contains(compiler.getOptions().getOutputFeatureSet())) {
Es6ToEs3Util.cannotConvert(
compiler, n, "ES5 getters/setters (consider using --language_out=ES5)");
}
Expand Down
3 changes: 1 addition & 2 deletions src/com/google/javascript/jscomp/Es6RewriteClass.java
Expand Up @@ -22,7 +22,6 @@

import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature;
import com.google.javascript.rhino.IR;
Expand Down Expand Up @@ -100,7 +99,7 @@ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
switch (n.getToken()) {
case GETTER_DEF:
case SETTER_DEF:
if (compiler.getOptions().getLanguageOut() == LanguageMode.ECMASCRIPT3) {
if (FeatureSet.ES3.contains(compiler.getOptions().getOutputFeatureSet())) {
cannotConvert(n, "ES5 getters/setters (consider using --language_out=ES5)");
return false;
}
Expand Down
Expand Up @@ -18,7 +18,6 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;

import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.jscomp.NodeUtil.Visitor;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature;
Expand Down Expand Up @@ -499,7 +498,7 @@ private void visitNewWithSpread(Node spreadParent) {
groups.toArray(new Node[0]))
.setJSType(arrayType);

if (compiler.getOptions().getLanguageOut() == LanguageMode.ECMASCRIPT3) {
if (FeatureSet.ES3.contains(compiler.getOptions().getOutputFeatureSet())) {
// TODO(tbreisacher): Support this in ES3 too by not relying on Function.bind.
Es6ToEs3Util.cannotConvert(
compiler,
Expand Down
3 changes: 1 addition & 2 deletions src/com/google/javascript/jscomp/LateEs6ToEs3Converter.java
Expand Up @@ -20,7 +20,6 @@
import static com.google.javascript.jscomp.Es6ToEs3Util.withType;

import com.google.common.collect.Lists;
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature;
import com.google.javascript.rhino.IR;
Expand Down Expand Up @@ -88,7 +87,7 @@ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
switch (n.getToken()) {
case GETTER_DEF:
case SETTER_DEF:
if (compiler.getOptions().getLanguageOut() == LanguageMode.ECMASCRIPT3) {
if (FeatureSet.ES3.contains(compiler.getOptions().getOutputFeatureSet())) {
Es6ToEs3Util.cannotConvert(
compiler, n, "ES5 getters/setters (consider using --language_out=ES5)");
return false;
Expand Down
4 changes: 2 additions & 2 deletions src/com/google/javascript/jscomp/RewritePolyfills.java
Expand Up @@ -247,7 +247,7 @@ public void visitGuarded(NodeTraversal traversal, Node node, Node parent) {
node,
INSUFFICIENT_OUTPUT_VERSION_ERROR,
name,
compiler.getOptions().getLanguageOut().toString());
compiler.getOptions().getOutputFeatureSet().version());
}
inject(polyfill);

Expand Down Expand Up @@ -292,7 +292,7 @@ private void inject(Polyfill polyfill) {
ImmutableSet.of("goog.global.", "window.");

private boolean languageOutIsAtLeast(LanguageMode mode) {
return compiler.getOptions().getLanguageOut().compareTo(mode) >= 0;
return compiler.getOptions().getOutputFeatureSet().contains(mode.toFeatureSet());
}

private boolean languageOutIsAtLeast(FeatureSet features) {
Expand Down
6 changes: 3 additions & 3 deletions src/com/google/javascript/jscomp/TranspilationPasses.java
Expand Up @@ -122,7 +122,7 @@ protected CompilerPass create(AbstractCompiler compiler) {

@Override
protected FeatureSet featureSet() {
return ES8_MODULES;
return FeatureSet.latest();
}
};

Expand Down Expand Up @@ -422,7 +422,7 @@ static boolean doesScriptHaveUnsupportedFeatures(Node script, FeatureSet support
static void processTranspile(
AbstractCompiler compiler, Node combinedRoot, FeatureSet featureSet, Callback... callbacks) {
if (compiler.getOptions().needsTranspilationFrom(featureSet)) {
FeatureSet languageOutFeatures = compiler.getOptions().getLanguageOut().toFeatureSet();
FeatureSet languageOutFeatures = compiler.getOptions().getOutputFeatureSet();
for (Node singleRoot : combinedRoot.children()) {

// Only run the transpilation if this file has features not in the compiler's target output
Expand Down Expand Up @@ -454,7 +454,7 @@ static void processTranspile(
static void hotSwapTranspile(
AbstractCompiler compiler, Node scriptRoot, FeatureSet featureSet, Callback... callbacks) {
if (compiler.getOptions().needsTranspilationFrom(featureSet)) {
FeatureSet languageOutFeatures = compiler.getOptions().getLanguageOut().toFeatureSet();
FeatureSet languageOutFeatures = compiler.getOptions().getOutputFeatureSet();
if (doesScriptHaveUnsupportedFeatures(scriptRoot, languageOutFeatures)) {
for (Callback callback : callbacks) {
scriptRoot.putBooleanProp(Node.TRANSPILED, true);
Expand Down

0 comments on commit 524c5fd

Please sign in to comment.