diff --git a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java index e6c823b3..adb01e05 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java @@ -344,6 +344,8 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { private boolean decoratorScope = false; + public Stack foreachLoopContext; + private final JSweetContext context; private final CompilationUnitTree compilationUnit; @@ -492,6 +494,17 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { } + private static class ForeachLoopScope { + public EnhancedForLoopTree loop; + public String variableName; + public String arrayName; + + public ForeachLoopScope(EnhancedForLoopTree loop) { + super(); + this.loop = loop; + } + } + private Stack scope = new Stack<>(); private boolean isAnnotationScope = false; @@ -5196,12 +5209,71 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { return returnNothing(); } + 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); + + Set generatedVariableNames = getGeneratedVariableNames(); + + while (generatedVariableNames.contains(name)) { + name = variablePrefix + (++index); + } + + int position = stack.size() - 2; + while (position >= 0 && !(stack.get(position) instanceof MethodTree)) { + if (stack.get(position) instanceof BlockTree) { + BlockTree block = (BlockTree) stack.get(position); + // analyze all the previous declarations in the block + for (Tree t : block.getStatements()) { + if (t == stack.get(position + 1)) { + // do not analyze post declarations + break; + } + // name clash: we try again with another index + if (t instanceof VariableTree && name.equals(((VariableTree) t).getName().toString())) { + return getFreeVariableName(variablePrefix, index + 1); + } + } + } + position--; + } + if (position >= 0 && stack.get(position) instanceof MethodTree) { + for (VariableTree param : ((MethodTree) stack.get(position)).getParameters()) { + if (name.equals(param.getName().toString())) { + return getFreeVariableName(variablePrefix, index + 1); + } + } + } + return name; + } + /** * Prints a foreach loop tree. */ @Override public Void visitEnhancedForLoop(EnhancedForLoopTree foreachLoop, Trees trees) { - String indexVarName = "index" + util().getId(); + String indexVarName = getFreeVariableName("index", getIndexVariableCount()); boolean[] hasLength = { false }; TypeElement targetType = Util.getTypeElement(foreachLoop.getExpression()); TypeMirror collectionType = Util.getType(foreachLoop.getExpression()); @@ -5221,20 +5293,27 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { }); } if (!getAdapter().substituteForEachLoop(createExtendedElement(foreachLoop), hasLength[0], indexVarName)) { - boolean noVariable = foreachLoop.getExpression() instanceof IdentifierTree + if (getScope().foreachLoopContext == null) { + getScope().foreachLoopContext = new Stack<>(); + } + ForeachLoopScope foreachLoopScope = new ForeachLoopScope(foreachLoop); + getScope().foreachLoopContext.push(foreachLoopScope); + boolean noVariable = foreachLoop.getExpression() instanceof IdentifierTree || foreachLoop.getExpression() instanceof MemberSelectTree; - if (noVariable) { - print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=0; " + indexVarName + " < ") + foreachLoopScope.variableName = indexVarName; + if (noVariable) { + print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + " = 0; " + indexVarName + " < ") .print(foreachLoop.getExpression()).print("." + "length" + "; " + indexVarName + "++) {") .println().startIndent().printIndent(); print(VAR_DECL_KEYWORD + " " + avoidJSKeyword(foreachLoop.getVariable().getName().toString()) + " = ") .print(foreachLoop.getExpression()).print("[" + indexVarName + "];").println(); } else { - String arrayVarName = "array" + util().getId(); - print("{").println().startIndent().printIndent(); + String arrayVarName = getFreeVariableName("array", getArrayVariableCount()); + foreachLoopScope.arrayName = arrayVarName; + print("{").println().startIndent().printIndent(); print(VAR_DECL_KEYWORD + " " + arrayVarName + " = ").print(foreachLoop.getExpression()).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.getVariable().getName().toString()) + " = " + arrayVarName + "[" + indexVarName + "];").println(); @@ -5245,6 +5324,7 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { if (!noVariable) { endIndent().println().printIndent().print("}"); } + getScope().foreachLoopContext.pop(); } return returnNothing(); 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 ec847b59..49eb6c45 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/util/Util.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/util/Util.java @@ -337,13 +337,6 @@ public class Util { private static long id = 121; - /** - * Returns a unique id (incremental). - */ - public 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 890a53be..7e94932e 100644 --- a/transpiler/src/test/java/source/syntax/Looping.java +++ b/transpiler/src/test/java/source/syntax/Looping.java @@ -78,9 +78,56 @@ public class Looping { lastIndex = j; } assert lastIndex == 602; + + 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); System.out.println("DONE Looping test"); } + + public static void loop(int[] array, int index) { + int pos = 1; + for (int integer : array) { + assert index == 14; + assert integer == pos; + pos++; + } + } int divide(int j) { return j / 2;