diff --git a/src/com/google/javascript/jscomp/DefaultPassConfig.java b/src/com/google/javascript/jscomp/DefaultPassConfig.java index 24978cbf1d4..26dd3f98244 100644 --- a/src/com/google/javascript/jscomp/DefaultPassConfig.java +++ b/src/com/google/javascript/jscomp/DefaultPassConfig.java @@ -172,6 +172,11 @@ protected List getTranspileOnlyPasses() { passes.add(setFeatureSet(ES7)); } + if (options.needsTranspilationFrom(ES7)) { + TranspilationPasses.addEs2016Passes(passes); + passes.add(setFeatureSet(ES6)); + } + // If the user has specified an input language of ES7 and an output language of ES6 or lower, // we still need to run these "ES6" passes, because they do the transpilation of the ES7 ** // operator. If we split that into its own pass then the needsTranspilationFrom(ES7) call here @@ -356,7 +361,12 @@ protected List getChecks() { checks.add(setFeatureSet(ES7)); } - if (options.needsTranspilationFrom(ES6) || options.needsTranspilationFrom(ES7)) { + if (options.needsTranspilationFrom(ES7)) { + TranspilationPasses.addEs2016Passes(checks); + checks.add(setFeatureSet(ES6)); + } + + if (options.needsTranspilationFrom(ES6)) { checks.add(es6ExternsCheck); TranspilationPasses.addEs6EarlyPasses(checks); } diff --git a/src/com/google/javascript/jscomp/Es6ToEs3Converter.java b/src/com/google/javascript/jscomp/Es6ToEs3Converter.java index 6009d1b8376..7becfb65e83 100644 --- a/src/com/google/javascript/jscomp/Es6ToEs3Converter.java +++ b/src/com/google/javascript/jscomp/Es6ToEs3Converter.java @@ -162,12 +162,6 @@ public void visit(NodeTraversal t, Node n, Node parent) { Es6TemplateLiterals.visitTemplateLiteral(t, n); } break; - case EXPONENT: - visitExponentiationExpression(n, parent); - break; - case ASSIGN_EXPONENT: - visitExponentiationAssignmentExpression(n, parent); - break; default: break; } @@ -195,25 +189,6 @@ private void initSymbolBefore(Node n) { compiler.reportChangeToEnclosingScope(initSymbol); } - private void visitExponentiationExpression(Node n, Node parent) { - Node left = n.removeFirstChild(); - Node right = n.removeFirstChild(); - Node mathDotPowCall = - IR.call(NodeUtil.newQName(compiler, "Math.pow"), left, right) - .useSourceInfoIfMissingFromForTree(n); - parent.replaceChild(n, mathDotPowCall); - compiler.reportChangeToEnclosingScope(mathDotPowCall); - } - - private void visitExponentiationAssignmentExpression(Node n, Node parent) { - Node left = n.removeFirstChild(); - Node right = n.removeFirstChild(); - Node mathDotPowCall = IR.call(NodeUtil.newQName(compiler, "Math.pow"), left.cloneTree(), right); - Node assign = IR.assign(left, mathDotPowCall).useSourceInfoIfMissingFromForTree(n); - parent.replaceChild(n, assign); - compiler.reportChangeToEnclosingScope(assign); - } - // TODO(tbreisacher): Do this for all well-known symbols. private void visitGetprop(NodeTraversal t, Node n) { if (!n.matchesQualifiedName("Symbol.iterator")) { diff --git a/src/com/google/javascript/jscomp/Es7ToEs6Converter.java b/src/com/google/javascript/jscomp/Es7ToEs6Converter.java new file mode 100644 index 00000000000..142eb498943 --- /dev/null +++ b/src/com/google/javascript/jscomp/Es7ToEs6Converter.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014 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; + +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; + +/** + * Converts ES7 code to valid ES6 code. + * + * Currently this class converts ** and **= operators to calling Math.pow + */ +public final class Es7ToEs6Converter implements NodeTraversal.Callback, HotSwapCompilerPass { + private final AbstractCompiler compiler; + + public Es7ToEs6Converter(AbstractCompiler compiler) { + this.compiler = compiler; + } + + @Override + public void process(Node externs, Node root) { + TranspilationPasses.processTranspile(compiler, externs, this); + TranspilationPasses.processTranspile(compiler, root, this); + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + TranspilationPasses.hotSwapTranspile(compiler, scriptRoot, this); + } + + @Override + public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { + return true; + } + + @Override + public void visit(NodeTraversal t, Node n, Node parent) { + switch (n.getToken()) { + case EXPONENT: + visitExponentiationExpression(n, parent); + break; + case ASSIGN_EXPONENT: + visitExponentiationAssignmentExpression(n, parent); + break; + default: + break; + } + } + + private void visitExponentiationExpression(Node n, Node parent) { + Node left = n.removeFirstChild(); + Node right = n.removeFirstChild(); + Node mathDotPowCall = + IR.call(NodeUtil.newQName(compiler, "Math.pow"), left, right) + .useSourceInfoIfMissingFromForTree(n); + parent.replaceChild(n, mathDotPowCall); + compiler.reportChangeToEnclosingScope(mathDotPowCall); + } + + private void visitExponentiationAssignmentExpression(Node n, Node parent) { + Node left = n.removeFirstChild(); + Node right = n.removeFirstChild(); + Node mathDotPowCall = IR.call(NodeUtil.newQName(compiler, "Math.pow"), left.cloneTree(), right); + Node assign = IR.assign(left, mathDotPowCall).useSourceInfoIfMissingFromForTree(n); + parent.replaceChild(n, assign); + compiler.reportChangeToEnclosingScope(assign); + } +} diff --git a/src/com/google/javascript/jscomp/TranspilationPasses.java b/src/com/google/javascript/jscomp/TranspilationPasses.java index 7a5805686c4..9b9db8184c1 100644 --- a/src/com/google/javascript/jscomp/TranspilationPasses.java +++ b/src/com/google/javascript/jscomp/TranspilationPasses.java @@ -38,6 +38,10 @@ public static void addEs2017Passes(List passes) { passes.add(rewriteAsyncFunctions); } + public static void addEs2016Passes(List passes) { + passes.add(convertEs7ToEs6); + } + /** * Adds all the early ES6 transpilation passes, which go before the Dart pass. * @@ -104,6 +108,19 @@ protected FeatureSet featureSet() { } }; + private static final PassFactory convertEs7ToEs6 = + new PassFactory("convertEs7ToEs6", true) { + @Override + protected CompilerPass create(final AbstractCompiler compiler) { + return new Es7ToEs6Converter(compiler); + } + + @Override + protected FeatureSet featureSet() { + return ES8; + } + }; + private static final PassFactory es6SuperCheck = new PassFactory("es6SuperCheck", true) { @Override diff --git a/test/com/google/javascript/jscomp/CompilerTestCase.java b/test/com/google/javascript/jscomp/CompilerTestCase.java index 471bbecdd06..65825f3b5fb 100644 --- a/test/com/google/javascript/jscomp/CompilerTestCase.java +++ b/test/com/google/javascript/jscomp/CompilerTestCase.java @@ -1684,6 +1684,7 @@ private static void transpileToEs5(AbstractCompiler compiler, Node externsRoot, List factories = new ArrayList<>(); TranspilationPasses.addEs6ModulePass(factories); TranspilationPasses.addEs2017Passes(factories); + TranspilationPasses.addEs2016Passes(factories); TranspilationPasses.addEs6EarlyPasses(factories); TranspilationPasses.addEs6LatePasses(factories); TranspilationPasses.addRewritePolyfillPass(factories); diff --git a/test/com/google/javascript/jscomp/Es6ToEs3ConverterTest.java b/test/com/google/javascript/jscomp/Es6ToEs3ConverterTest.java index 9e965f00b12..715b8a09f76 100644 --- a/test/com/google/javascript/jscomp/Es6ToEs3ConverterTest.java +++ b/test/com/google/javascript/jscomp/Es6ToEs3ConverterTest.java @@ -262,14 +262,6 @@ public void testAnonymousSuper() { "f(testcode$classdecl$var0)")); } - public void testExponentiationOperator() { - test("2 ** 2;", "Math.pow(2,2)"); - } - - public void testExponentiationAssignmentOperator() { - test("x **= 2;", "x=Math.pow(x,2)"); - } - public void testNewTarget() { testError("function Foo() { new.target; }", CANNOT_CONVERT_YET); } diff --git a/test/com/google/javascript/jscomp/Es7ToEs6ConverterTest.java b/test/com/google/javascript/jscomp/Es7ToEs6ConverterTest.java new file mode 100644 index 00000000000..03e1e683573 --- /dev/null +++ b/test/com/google/javascript/jscomp/Es7ToEs6ConverterTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2014 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; + +import com.google.javascript.jscomp.CompilerOptions.LanguageMode; + +/** + * Test cases for ES6 transpilation. Despite the name, this isn't just testing {@link + * Es6ToEs3Converter}, but also some other ES6 transpilation passes. See #getProcessor. + */ +public final class Es7ToEs6ConverterTest extends TypeICompilerTestCase { + + public Es7ToEs6ConverterTest() { + super(MINIMAL_EXTERNS); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setAcceptedLanguage(LanguageMode.ECMASCRIPT_2016); + setLanguageOut(LanguageMode.ECMASCRIPT5); + enableRunTypeCheckAfterProcessing(); + this.mode = TypeInferenceMode.NEITHER; + } + + @Override + protected CompilerPass getProcessor(Compiler compiler) { + return new Es7ToEs6Converter(compiler); + } + + @Override + protected int getNumRepetitions() { + return 1; + } + + public void testExponentiationOperator() { + test("2 ** 2;", "Math.pow(2,2)"); + } + + public void testExponentiationAssignmentOperator() { + test("x **= 2;", "x=Math.pow(x,2)"); + } +} diff --git a/test/com/google/javascript/jscomp/NewTypeInferenceTestBase.java b/test/com/google/javascript/jscomp/NewTypeInferenceTestBase.java index ca0780a1b1a..697ff8061e8 100644 --- a/test/com/google/javascript/jscomp/NewTypeInferenceTestBase.java +++ b/test/com/google/javascript/jscomp/NewTypeInferenceTestBase.java @@ -247,6 +247,7 @@ private final void parseAndTypeCheck(String externs, String js) { } if (compilerOptions.needsTranspilationFrom(FeatureSet.ES6)) { TranspilationPasses.addEs2017Passes(passes); + TranspilationPasses.addEs2016Passes(passes); TranspilationPasses.addEs6EarlyPasses(passes); TranspilationPasses.addEs6LatePasses(passes); TranspilationPasses.addRewritePolyfillPass(passes); diff --git a/test/com/google/javascript/jscomp/TypeCheckTest.java b/test/com/google/javascript/jscomp/TypeCheckTest.java index dfbdbfdd5af..b53553801f6 100644 --- a/test/com/google/javascript/jscomp/TypeCheckTest.java +++ b/test/com/google/javascript/jscomp/TypeCheckTest.java @@ -17987,6 +17987,7 @@ private TypeCheckResult parseAndTypeCheckWithScope(String externs, String js) { List passes = new ArrayList<>(); TranspilationPasses.addEs6ModulePass(passes); TranspilationPasses.addEs2017Passes(passes); + TranspilationPasses.addEs2016Passes(passes); TranspilationPasses.addEs6EarlyPasses(passes); TranspilationPasses.addEs6LatePasses(passes); TranspilationPasses.addRewritePolyfillPass(passes);