diff --git a/src/com/google/javascript/jscomp/Es7RewriteExponentialOperator.java b/src/com/google/javascript/jscomp/Es7RewriteExponentialOperator.java new file mode 100644 index 00000000000..12bc38eeebd --- /dev/null +++ b/src/com/google/javascript/jscomp/Es7RewriteExponentialOperator.java @@ -0,0 +1,127 @@ +/* + * 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.parsing.parser.FeatureSet; +import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature; +import com.google.javascript.rhino.IR; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeNative; +import com.google.javascript.rhino.jstype.JSTypeRegistry; + +/** Replaces the ES7 `**` and `**=` operators to calls to `Math.pow`. */ +public final class Es7RewriteExponentialOperator + implements NodeTraversal.Callback, HotSwapCompilerPass { + + private static final FeatureSet transpiledFeatures = + FeatureSet.BARE_MINIMUM.with(Feature.EXPONENT_OP); + + private final AbstractCompiler compiler; + private final Node mathPowCall; // This node should only ever be cloned, not directly inserted. + + private final JSType numberType; + private final JSType stringType; + private final JSType mathType; + private final JSType mathPowType; + + public Es7RewriteExponentialOperator(AbstractCompiler compiler) { + this.compiler = compiler; + + if (compiler.hasTypeCheckingRun()) { + JSTypeRegistry registry = compiler.getTypeRegistry(); + this.numberType = registry.getNativeType(JSTypeNative.NUMBER_TYPE); + this.stringType = registry.getNativeType(JSTypeNative.STRING_TYPE); + // TODO(nickreid): Get the actual type of the `Math` object here in case optimizations care. + this.mathType = registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); + this.mathPowType = registry.createFunctionType(numberType, numberType, numberType); + } else { + this.numberType = null; + this.stringType = null; + this.mathType = null; + this.mathPowType = null; + } + + this.mathPowCall = createMathPowCall(); + } + + @Override + public void process(Node externs, Node root) { + TranspilationPasses.processTranspile(compiler, externs, transpiledFeatures, this); + TranspilationPasses.processTranspile(compiler, root, transpiledFeatures, this); + TranspilationPasses.markFeaturesAsTranspiledAway(compiler, transpiledFeatures); + } + + @Override + public void hotSwapScript(Node scriptRoot, Node originalRoot) { + TranspilationPasses.hotSwapTranspile(compiler, scriptRoot, transpiledFeatures, this); + TranspilationPasses.markFeaturesAsTranspiledAway(compiler, transpiledFeatures); + } + + @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: + visitExponentiationOperator(n); + break; + case ASSIGN_EXPONENT: + visitExponentiationAssignmentOperator(n); + break; + default: + break; + } + } + + private void visitExponentiationOperator(Node operator) { + Node callClone = mathPowCall.cloneTree(); + callClone.addChildToBack(operator.removeFirstChild()); // Base argument. + callClone.addChildToBack(operator.removeFirstChild()); // Exponent argument. + + callClone.useSourceInfoIfMissingFromForTree(operator); + operator.replaceWith(callClone); + + compiler.reportChangeToEnclosingScope(callClone); + } + + private void visitExponentiationAssignmentOperator(Node operator) { + Node lValue = operator.removeFirstChild(); + + Node callClone = mathPowCall.cloneTree(); + callClone.addChildToBack(lValue.cloneTree()); // Base argument. + callClone.addChildToBack(operator.removeFirstChild()); // Exponent argument. + + Node assignment = IR.assign(lValue, callClone).setJSType(numberType); + + assignment.useSourceInfoIfMissingFromForTree(operator); + operator.replaceWith(assignment); + + compiler.reportChangeToEnclosingScope(assignment); + } + + private Node createMathPowCall() { + return IR.call( + IR.getprop( + IR.name("Math").setJSType(mathType), // Force wrapping. + IR.string("pow").setJSType(stringType)) + .setJSType(mathPowType)) + .setJSType(numberType); + } +} diff --git a/src/com/google/javascript/jscomp/Es7ToEs6Converter.java b/src/com/google/javascript/jscomp/Es7ToEs6Converter.java deleted file mode 100644 index df41a23ed30..00000000000 --- a/src/com/google/javascript/jscomp/Es7ToEs6Converter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 static com.google.javascript.jscomp.Es6ToEs3Util.createType; -import static com.google.javascript.jscomp.Es6ToEs3Util.withType; - -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; -import com.google.javascript.jscomp.parsing.parser.FeatureSet; -import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature; -import com.google.javascript.rhino.IR; -import com.google.javascript.rhino.Node; -import com.google.javascript.rhino.jstype.JSType; -import com.google.javascript.rhino.jstype.JSTypeNative; - -/** - * 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; - private static final FeatureSet transpiledFeatures = - FeatureSet.BARE_MINIMUM.with(Feature.EXPONENT_OP); - private final boolean addTypes; - private final Supplier mathPow = Suppliers.memoize(new MathPowSupplier()); - - public Es7ToEs6Converter(AbstractCompiler compiler) { - this.compiler = compiler; - this.addTypes = compiler.hasTypeCheckingRun(); - } - - @Override - public void process(Node externs, Node root) { - TranspilationPasses.processTranspile(compiler, externs, transpiledFeatures, this); - TranspilationPasses.processTranspile(compiler, root, transpiledFeatures, this); - TranspilationPasses.markFeaturesAsTranspiledAway(compiler, transpiledFeatures); - } - - @Override - public void hotSwapScript(Node scriptRoot, Node originalRoot) { - TranspilationPasses.hotSwapTranspile(compiler, scriptRoot, transpiledFeatures, this); - TranspilationPasses.markFeaturesAsTranspiledAway(compiler, transpiledFeatures); - } - - @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 = - withType(IR.call(mathPow.get().cloneTree(), left, right), n.getJSType()) - .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 = - withType(IR.call(mathPow.get().cloneTree(), left.cloneTree(), right), n.getJSType()); - Node assign = - withType(IR.assign(left, mathDotPowCall), n.getJSType()) - .useSourceInfoIfMissingFromForTree(n); - parent.replaceChild(n, assign); - compiler.reportChangeToEnclosingScope(assign); - } - - private class MathPowSupplier implements Supplier { - @Override public Node get() { - Node n = NodeUtil.newQName(compiler, "Math.pow"); - if (addTypes) { - JSType mathType = compiler.getTypeRegistry().getGlobalType("Math"); - JSType mathPowType = mathType.toMaybeObjectType().getPropertyType("pow"); - JSType stringType = - createType(addTypes, compiler.getTypeRegistry(), JSTypeNative.STRING_TYPE); - n.setJSType(mathPowType); - n.getFirstChild().setJSType(mathType); - n.getSecondChild().setJSType(stringType); - } - return n; - } - } -} diff --git a/src/com/google/javascript/jscomp/TranspilationPasses.java b/src/com/google/javascript/jscomp/TranspilationPasses.java index 2d1f60dd032..6a830bd8e8b 100644 --- a/src/com/google/javascript/jscomp/TranspilationPasses.java +++ b/src/com/google/javascript/jscomp/TranspilationPasses.java @@ -156,7 +156,7 @@ protected FeatureSet featureSet() { new HotSwapPassFactory("convertEs7ToEs6") { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { - return new Es7ToEs6Converter(compiler); + return new Es7RewriteExponentialOperator(compiler); } @Override diff --git a/test/com/google/javascript/jscomp/Es7ToEs6ConverterTest.java b/test/com/google/javascript/jscomp/Es7RewriteExponentialOperatorTest.java similarity index 89% rename from test/com/google/javascript/jscomp/Es7ToEs6ConverterTest.java rename to test/com/google/javascript/jscomp/Es7RewriteExponentialOperatorTest.java index de05243d657..f08d3d399f3 100644 --- a/test/com/google/javascript/jscomp/Es7ToEs6ConverterTest.java +++ b/test/com/google/javascript/jscomp/Es7RewriteExponentialOperatorTest.java @@ -21,9 +21,9 @@ * 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 CompilerTestCase { +public final class Es7RewriteExponentialOperatorTest extends CompilerTestCase { - public Es7ToEs6ConverterTest() { + public Es7RewriteExponentialOperatorTest() { super(MINIMAL_EXTERNS); } @@ -37,7 +37,7 @@ protected void setUp() throws Exception { @Override protected CompilerPass getProcessor(Compiler compiler) { - return new Es7ToEs6Converter(compiler); + return new Es7RewriteExponentialOperator(compiler); } @Override