diff --git a/transpiler/src/main/java/org/jsweet/transpiler/ConstAnalyzer.java b/transpiler/src/main/java/org/jsweet/transpiler/ConstAnalyzer.java new file mode 100644 index 00000000..b451a17f --- /dev/null +++ b/transpiler/src/main/java/org/jsweet/transpiler/ConstAnalyzer.java @@ -0,0 +1,99 @@ +/* + * JSweet transpiler - http://www.jsweet.org + * Copyright (C) 2015 CINCHEO SAS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +package org.jsweet.transpiler; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.lang.model.element.ElementKind; + +import org.jsweet.transpiler.util.AbstractTreeScanner; + +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAssign; +import com.sun.tools.javac.tree.JCTree.JCAssignOp; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCUnary; + +/** + * This AST scanner analyzes local variables to determine if they are locally + * assigned and can be assumed constant or not. + * + * @see JSweetContext + * @author Renaud Pawlak + */ +public class ConstAnalyzer extends AbstractTreeScanner { + + private Set modifiedVariables = new HashSet<>(); + + /** + * Gets all the local variables that are modified within the program. All other + * local variables can be assumed as constant. + */ + public Set getModifiedVariables() { + return modifiedVariables; + } + + /** + * Creates a new constant variable analyzer. + */ + public ConstAnalyzer(TranspilationHandler logHandler, JSweetContext context) { + super(logHandler, context, null); + } + + private void registerModification(JCTree tree) { + if (tree instanceof JCIdent && ((JCIdent) tree).sym.getKind() == ElementKind.LOCAL_VARIABLE) { + modifiedVariables.add((VarSymbol) ((JCIdent) tree).sym); + } + } + + @Override + public void visitAssign(JCAssign assign) { + registerModification(assign.lhs); + super.visitAssign(assign); + } + + @Override + public void visitAssignop(JCAssignOp assignOp) { + registerModification(assignOp.lhs); + } + + @Override + public void visitUnary(JCUnary unary) { + switch (unary.getTag()) { + case PREINC: + case POSTINC: + case PREDEC: + case POSTDEC: + registerModification(unary.arg); + default: + } + super.visitUnary(unary); + } + + public void process(List compilationUnits) { + for (JCCompilationUnit compilationUnit : compilationUnits) { + scan(compilationUnit); + } + } + +} diff --git a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java index 4f16530c..b74c2708 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java @@ -482,6 +482,8 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { private boolean isDefinitionScope = false; + private ConstAnalyzer constAnalyzer = null; + protected final boolean isTopLevelScope() { return getIndent() == 0; } @@ -736,7 +738,9 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { footer.delete(0, footer.length()); setCompilationUnit(topLevel); - + constAnalyzer = new ConstAnalyzer(getLogHandler(), context); + constAnalyzer.scan(topLevel); + String packge = topLevel.packge.toString(); boolean globalModule = JSweetConfig.GLOBALS_PACKAGE_NAME.equals(packge) @@ -3404,7 +3408,12 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { if (isDefinitionScope) { print("var "); } else { - print(VAR_DECL_KEYWORD + " "); + if (varDecl.sym.getModifiers().contains(Modifier.FINAL) || (constAnalyzer != null + && !constAnalyzer.getModifiedVariables().contains(varDecl.sym))) { + print("const "); + } else { + print(VAR_DECL_KEYWORD + " "); + } } } } else { diff --git a/transpiler/src/main/java/org/jsweet/transpiler/util/AbstractTreeScanner.java b/transpiler/src/main/java/org/jsweet/transpiler/util/AbstractTreeScanner.java index 1cdae5f8..f1a3528c 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/util/AbstractTreeScanner.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/util/AbstractTreeScanner.java @@ -60,7 +60,7 @@ public abstract class AbstractTreeScanner extends TreeScanner { private TranspilationHandler logHandler; - /** + /** * Report a JSweet problem on the given program element (tree). * * @param tree @@ -468,4 +468,11 @@ public abstract class AbstractTreeScanner extends TreeScanner { return null; } + /** + * Gets the transpilation handler for this scanner. + */ + public TranspilationHandler getLogHandler() { + return logHandler; + } + } diff --git a/transpiler/src/test/java/source/syntax/FinalVariables.java b/transpiler/src/test/java/source/syntax/FinalVariables.java index c900ff21..8c4cfb2f 100644 --- a/transpiler/src/test/java/source/syntax/FinalVariables.java +++ b/transpiler/src/test/java/source/syntax/FinalVariables.java @@ -33,7 +33,11 @@ import def.js.Math; public class FinalVariables { + int finalField; + int modifiedField; + void m1(HTMLElement e) { + modifiedField++; NodeList nodes = document.querySelectorAll("form .form-control"); for (int i = 0; i < nodes.length; i++) { HTMLElement element = (HTMLElement) nodes.$get(i); @@ -117,6 +121,12 @@ public class FinalVariables { }); } + String notFinal() { + String s = "abc"; + s = "bcd"; + return s; + } + } // subset of promises API