From 11e4b53f6d7f1ad8fe94b9afa274bfbbb57f7a11 Mon Sep 17 00:00:00 2001 From: Renaud Pawlak Date: Wed, 6 Jan 2021 19:05:09 +0100 Subject: [PATCH] generate human-friendly lopping variable names --- .../transpiler/Java2TypeScriptTranslator.java | 92 ++++++++++++++++++- .../java/org/jsweet/transpiler/util/Util.java | 9 -- .../src/test/java/source/syntax/Looping.java | 46 ++++++++++ 3 files changed, 133 insertions(+), 14 deletions(-) diff --git a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java index 1a9e6fcf..56a0b407 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java @@ -87,6 +87,7 @@ import org.jsweet.transpiler.util.JSDoc; import org.jsweet.transpiler.util.RollbackException; import org.jsweet.transpiler.util.Util; +import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.code.Attribute; import com.sun.tools.javac.code.Attribute.Compound; @@ -361,7 +362,9 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { private boolean constructor = false; private boolean decoratorScope = false; - + + public Stack foreachLoopContext; + public String getName() { return name; } @@ -480,6 +483,16 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { } + private static class ForeachLoopScope { + public JCEnhancedForLoop loop; + public String variableName; + public String arrayName; + public ForeachLoopScope(JCEnhancedForLoop loop) { + super(); + this.loop = loop; + } + } + private Stack scope = new Stack<>(); private boolean isAnnotationScope = false; @@ -4868,12 +4881,73 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { } } + private int getIndexVariableCount() { + return getScope().foreachLoopContext == null ? 0 : getScope().foreachLoopContext.size(); + } + + private Set getGeneratedVariableNames() { + Set names = new HashSet<>(); + if (getScope().foreachLoopContext != null) { + getScope().foreachLoopContext.stream().forEach(s -> { + names.add(s.variableName); + if (s.arrayName != null) { + names.add(s.arrayName); + } + }); + } + return names; + } + + private int getArrayVariableCount() { + return getScope().foreachLoopContext == null ? 0 + : (int) getScope().foreachLoopContext.stream().filter(s -> s.arrayName != null).count(); + } + + private String getFreeVariableName(String variablePrefix, int index) { + String name = variablePrefix + (index == 0 ? "" : "" + index); + System.out.println("candidate name: " + name); + + Set generatedVariableNames = getGeneratedVariableNames(); + + while (generatedVariableNames.contains(name)) { + name = variablePrefix + (++index); + } + + int position = stack.size() - 2; + while(position >= 0 && !(stack.get(position) instanceof JCMethodDecl)) { + if (stack.get(position) instanceof JCBlock) { + JCBlock block = (JCBlock)stack.get(position); + // analyze all the previous declarations in the block + for (JCTree t : block.stats) { + if (t == stack.get(position + 1)) { + // do not analyze post declarations + break; + } + // name clash: we try again with another index + if (t instanceof JCVariableDecl && name.equals(((JCVariableDecl)t).name.toString())) { + return getFreeVariableName(variablePrefix, index + 1); + } + } + } + position--; + } + if (position >= 0 && stack.get(position) instanceof JCMethodDecl) { + for (JCVariableDecl param : ((JCMethodDecl)stack.get(position)).params) { + if (name.equals(param.name.toString())) { + // name clash with method parameters + return getFreeVariableName(variablePrefix, index + 1); + } + } + } + return name; + } + /** * Prints a foreach loop tree. */ @Override public void visitForeachLoop(JCEnhancedForLoop foreachLoop) { - String indexVarName = "index" + Util.getId(); + String indexVarName = getFreeVariableName("index", getIndexVariableCount()); boolean[] hasLength = { false }; TypeSymbol targetType = foreachLoop.expr.type.tsym; Util.scanMemberDeclarationsInType(targetType, getAdapter().getErasedTypes(), element -> { @@ -4887,19 +4961,26 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { }); if (!getAdapter().substituteForEachLoop(new ForeachLoopElementSupport(foreachLoop), hasLength[0], indexVarName)) { + if (getScope().foreachLoopContext == null) { + getScope().foreachLoopContext = new Stack<>(); + } + ForeachLoopScope foreachLoopScope = new ForeachLoopScope(foreachLoop); + getScope().foreachLoopContext.push(foreachLoopScope); boolean noVariable = foreachLoop.expr instanceof JCIdent || foreachLoop.expr instanceof JCFieldAccess; + foreachLoopScope.variableName = indexVarName; if (noVariable) { - print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=0; " + indexVarName + " < ") + print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + " = 0; " + indexVarName + " < ") .print(foreachLoop.expr).print("." + "length" + "; " + indexVarName + "++) {").println() .startIndent().printIndent(); print(VAR_DECL_KEYWORD + " " + avoidJSKeyword(foreachLoop.var.name.toString()) + " = ") .print(foreachLoop.expr).print("[" + indexVarName + "];").println(); } else { - String arrayVarName = "array" + Util.getId(); + String arrayVarName = getFreeVariableName("array", getArrayVariableCount()); + foreachLoopScope.arrayName = arrayVarName; print("{").println().startIndent().printIndent(); print(VAR_DECL_KEYWORD + " " + arrayVarName + " = ").print(foreachLoop.expr).print(";").println() .printIndent(); - print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=0; " + indexVarName + " < " + arrayVarName + print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + " = 0; " + indexVarName + " < " + arrayVarName + ".length; " + indexVarName + "++) {").println().startIndent().printIndent(); print(VAR_DECL_KEYWORD + " " + avoidJSKeyword(foreachLoop.var.name.toString()) + " = " + arrayVarName + "[" + indexVarName + "];").println(); @@ -4910,6 +4991,7 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { if (!noVariable) { endIndent().println().printIndent().print("}"); } + getScope().foreachLoopContext.pop(); } } diff --git a/transpiler/src/main/java/org/jsweet/transpiler/util/Util.java b/transpiler/src/main/java/org/jsweet/transpiler/util/Util.java index 1be46d5f..d9de6c41 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/util/Util.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/util/Util.java @@ -111,15 +111,6 @@ public class Util { private Util() { } - private static long id = 121; - - /** - * Returns a unique id (incremental). - */ - public static long getId() { - return id++; - } - /** * Tells if the given element is within the Java sources being compiled. */ diff --git a/transpiler/src/test/java/source/syntax/Looping.java b/transpiler/src/test/java/source/syntax/Looping.java index 46c28f95..bfef3c83 100644 --- a/transpiler/src/test/java/source/syntax/Looping.java +++ b/transpiler/src/test/java/source/syntax/Looping.java @@ -61,8 +61,54 @@ public class Looping { System.out.println(">"+i); assert i==-1; + int[] array = new int[] { 1, 2, 3 }; + int pos = 1; + for (int integer : array) { + assert integer == pos; + pos++; + } + + pos = 1; + for (int integer : array) { + assert integer == pos; + pos++; + } + + // nested loops + pos = 1; + for (int integer : array) { + assert integer == pos; + pos++; + int pos2 = 1; + int index1 = 4; + for (int integer2 : array) { + assert integer2 == pos2; + assert index1 == 4; + pos2++; + } + } + + // test clash between iteration variable and local variable + int index = 12; + pos = 1; + for (int integer : array) { + assert index == 12; + assert integer == pos; + pos++; + } + + loop(array, 14); } + public static void loop(int[] array, int index) { + int pos = 1; + for (int integer : array) { + assert index == 14; + assert integer == pos; + pos++; + } + } + public static > T max(Collection coll) { Collection c = null; // TODO: Java accepts this assignment without casting but TypeScript