diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index a4804cde434..fce40d5a6e0 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -30,6 +30,7 @@ import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.BitSet; +import java.util.Iterator; import java.util.Map; /** @@ -56,6 +57,10 @@ public final class WriterConstants { public final static Type MAP_TYPE = Type.getType(Map.class); public final static Method MAP_GET = getAsmMethod(Object.class, "get", Object.class); + public final static Type ITERATOR_TYPE = Type.getType(Iterator.class); + public final static Method ITERATOR_HASNEXT = getAsmMethod(boolean.class, "hasNext"); + public final static Method ITERATOR_NEXT = getAsmMethod(Object.class, "next"); + public final static Type UTILITY_TYPE = Type.getType(Utility.class); public final static Method STRING_TO_CHAR = getAsmMethod(char.class, "StringTochar", String.class); public final static Method CHAR_TO_STRING = getAsmMethod(String.class, "charToString", char.class); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java index 3d6d5e07fdc..2a8fb0e4063 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java @@ -35,6 +35,9 @@ import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE; +import static org.elasticsearch.painless.WriterConstants.ITERATOR_HASNEXT; +import static org.elasticsearch.painless.WriterConstants.ITERATOR_NEXT; +import static org.elasticsearch.painless.WriterConstants.ITERATOR_TYPE; /** * Represents a for-each loop shortcut for iterables. Defers to other S-nodes for non-iterable types. @@ -59,8 +62,6 @@ public class SEach extends AStatement { // Members for the iterable case. Variable iterator = null; Method method = null; - Method hasNext = null; - Method next = null; public SEach(Location location, int maxLoopCounter, String type, String name, AExpression expression, SBlock block) { super(location); @@ -78,16 +79,6 @@ public class SEach extends AStatement { expression.expected = expression.actual; expression = expression.cast(variables); - if (expression.actual.sort == Sort.ARRAY) { - analyzeArray(variables); - } else if (expression.actual.sort == Sort.DEF || Iterable.class.isAssignableFrom(expression.actual.clazz)) { - analyzeIterable(variables); - } else { - throw location.createError(new IllegalArgumentException("Illegal for each type [" + expression.actual.name + "].")); - } - } - - void analyzeArray(Variables variables) { final Type type; try { @@ -99,10 +90,14 @@ public class SEach extends AStatement { variables.incrementScope(); variable = variables.addVariable(location, type, name, true, false); - array = variables.addVariable(location, expression.actual, "#array" + location.getOffset(), true, false); - index = variables.addVariable(location, Definition.INT_TYPE, "#index" + location.getOffset(), true, false); - indexed = Definition.getType(expression.actual.struct, expression.actual.dimensions - 1); - cast = AnalyzerCaster.getLegalCast(location, indexed, type, true, true); + + if (expression.actual.sort == Sort.ARRAY) { + analyzeArray(variables, type); + } else if (expression.actual.sort == Sort.DEF || Iterable.class.isAssignableFrom(expression.actual.clazz)) { + analyzeIterable(variables, type); + } else { + throw location.createError(new IllegalArgumentException("Illegal for each type [" + expression.actual.name + "].")); + } if (block == null) { throw location.createError(new IllegalArgumentException("Extraneous for each loop.")); @@ -126,24 +121,19 @@ public class SEach extends AStatement { variables.decrementScope(); } - void analyzeIterable(Variables variables) { - final Type type; - - try { - type = Definition.getType(this.type); - } catch (IllegalArgumentException exception) { - throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); - } - - variables.incrementScope(); - - Type itr = Definition.getType("Iterator"); - - variable = variables.addVariable(location, type, name, true, false); + void analyzeArray(Variables variables, Type type) { + // We must store the array and index as variables for securing slots on the stack, and + // also add the location offset to make the names unique in case of nested for each loops. + array = variables.addVariable(location, expression.actual, "#array" + location.getOffset(), true, false); + index = variables.addVariable(location, Definition.INT_TYPE, "#index" + location.getOffset(), true, false); + indexed = Definition.getType(expression.actual.struct, expression.actual.dimensions - 1); + cast = AnalyzerCaster.getLegalCast(location, indexed, type, true, true); + } + void analyzeIterable(Variables variables, Type type) { // We must store the iterator as a variable for securing a slot on the stack, and // also add the location offset to make the name unique in case of nested for each loops. - iterator = variables.addVariable(location, itr, "#itr" + location.getOffset(), true, false); + iterator = variables.addVariable(location, Definition.getType("Iterator"), "#itr" + location.getOffset(), true, false); if (expression.actual.sort == Sort.DEF) { method = null; @@ -156,48 +146,13 @@ public class SEach extends AStatement { } } - hasNext = itr.struct.methods.get(new MethodKey("hasNext", 0)); - - if (hasNext == null) { - throw location.createError(new IllegalArgumentException("Method [hasNext] does not exist for type [Iterator].")); - } else if (hasNext.rtn.sort != Sort.BOOL) { - throw location.createError(new IllegalArgumentException("Method [hasNext] does not return type [boolean].")); - } - - next = itr.struct.methods.get(new MethodKey("next", 0)); - - if (next == null) { - throw location.createError(new IllegalArgumentException("Method [next] does not exist for type [Iterator].")); - } else if (next.rtn.sort != Sort.DEF) { - throw location.createError(new IllegalArgumentException("Method [next] does not return type [def].")); - } - cast = AnalyzerCaster.getLegalCast(location, Definition.DEF_TYPE, type, true, true); - - if (block == null) { - throw location.createError(new IllegalArgumentException("Extraneous for each loop.")); - } - - block.beginLoop = true; - block.inLoop = true; - block.analyze(variables); - block.statementCount = Math.max(1, block.statementCount); - - if (block.loopEscape && !block.anyContinue) { - throw createError(new IllegalArgumentException("Extraneous for loop.")); - } - - statementCount = 1; - - if (maxLoopCounter > 0) { - loopCounterSlot = variables.getVariable(location, "#loop").slot; - } - - variables.decrementScope(); } @Override void write(MethodWriter writer) { + writer.writeStatementOffset(location); + if (array != null) { writeArray(writer); } else if (iterator != null) { @@ -208,8 +163,6 @@ public class SEach extends AStatement { } void writeArray(MethodWriter writer) { - writer.writeStatementOffset(location); - expression.write(writer); writer.visitVarInsn(array.type.type.getOpcode(Opcodes.ISTORE), array.slot); writer.push(-1); @@ -239,8 +192,6 @@ public class SEach extends AStatement { } void writeIterable(MethodWriter writer) { - writer.writeStatementOffset(location); - expression.write(writer); if (method == null) { @@ -261,11 +212,11 @@ public class SEach extends AStatement { writer.mark(begin); writer.visitVarInsn(iterator.type.type.getOpcode(Opcodes.ILOAD), iterator.slot); - writer.invokeInterface(hasNext.owner.type, hasNext.method); + writer.invokeInterface(ITERATOR_TYPE, ITERATOR_HASNEXT); writer.ifZCmp(MethodWriter.EQ, end); writer.visitVarInsn(iterator.type.type.getOpcode(Opcodes.ILOAD), iterator.slot); - writer.invokeInterface(next.owner.type, next.method); + writer.invokeInterface(ITERATOR_TYPE, ITERATOR_NEXT); writer.writeCast(cast); writer.visitVarInsn(variable.type.type.getOpcode(Opcodes.ISTORE), variable.slot);