Browse Source

Painless mechanical changes to support external passes (#56784)

These changes are made in mind to support "external" compiler passes that 
hava a user tree as purely data using decorations moving forward. The changes 
are mostly mechanical.

The following has been modified/added:

* Now that the user tree has a set number of nodes and the structure is 
immutable, each node is assigned an id. This id is intended for use when 
looking up future decorations to avoid hash look ups against user tree nodes. 
The range of the ids is [0, number-of-nodes). This allows an array-style look up 
for nodes in the tree.
* All members of the user tree node are made private, and getters are added 
for each of these. This allows external passes to access appropriate members 
that are fully immutable.
* Some member names for the user tree nodes have changed to be more easily 
understood.
* EVariable is changed to ESymbol since we no longer know if this node is a type 
or variable or package name.
Jack Conradson 5 years ago
parent
commit
4301e6ec17
51 changed files with 1219 additions and 811 deletions
  1. 79 67
      modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java
  2. 2 13
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AExpression.java
  3. 21 6
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ANode.java
  4. 2 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AStatement.java
  5. 38 26
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EAssignment.java
  6. 24 12
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java
  7. 24 12
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBool.java
  8. 12 8
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBoolean.java
  9. 27 19
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBrace.java
  10. 50 32
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECall.java
  11. 30 22
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java
  12. 25 13
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EComp.java
  13. 27 15
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConditional.java
  14. 11 5
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConstant.java
  15. 21 17
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EDecimal.java
  16. 48 36
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EDot.java
  17. 23 18
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EElvis.java
  18. 15 7
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java
  19. 34 26
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java
  20. 22 13
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java
  21. 37 23
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java
  22. 14 10
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java
  23. 26 18
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java
  24. 28 16
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java
  25. 14 14
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArrayFunctionRef.java
  26. 27 19
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java
  27. 3 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENull.java
  28. 33 25
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENumeric.java
  29. 23 15
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ERegex.java
  30. 12 8
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EString.java
  31. 17 13
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ESymbol.java
  32. 30 20
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EUnary.java
  33. 14 9
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBlock.java
  34. 3 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBreak.java
  35. 26 15
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java
  36. 13 8
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SClass.java
  37. 3 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SContinue.java
  38. 12 8
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclBlock.java
  39. 33 17
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java
  40. 21 13
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDo.java
  41. 42 26
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java
  42. 12 8
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SExpression.java
  43. 47 29
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFor.java
  44. 87 49
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java
  45. 20 12
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIf.java
  46. 28 16
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIfElse.java
  47. 14 10
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SReturn.java
  48. 11 7
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SThrow.java
  49. 11 10
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/STry.java
  50. 22 14
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SWhile.java
  51. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java

+ 79 - 67
modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java

@@ -139,8 +139,8 @@ import org.elasticsearch.painless.node.ENull;
 import org.elasticsearch.painless.node.ENumeric;
 import org.elasticsearch.painless.node.ENumeric;
 import org.elasticsearch.painless.node.ERegex;
 import org.elasticsearch.painless.node.ERegex;
 import org.elasticsearch.painless.node.EString;
 import org.elasticsearch.painless.node.EString;
+import org.elasticsearch.painless.node.ESymbol;
 import org.elasticsearch.painless.node.EUnary;
 import org.elasticsearch.painless.node.EUnary;
-import org.elasticsearch.painless.node.EVariable;
 import org.elasticsearch.painless.node.SBlock;
 import org.elasticsearch.painless.node.SBlock;
 import org.elasticsearch.painless.node.SBreak;
 import org.elasticsearch.painless.node.SBreak;
 import org.elasticsearch.painless.node.SCatch;
 import org.elasticsearch.painless.node.SCatch;
@@ -178,11 +178,19 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
     private final CompilerSettings settings;
     private final CompilerSettings settings;
     private final String sourceName;
     private final String sourceName;
 
 
+    private int identifier;
+
     private Walker(ScriptClassInfo scriptClassInfo, String sourceName, String sourceText, CompilerSettings settings) {
     private Walker(ScriptClassInfo scriptClassInfo, String sourceName, String sourceText, CompilerSettings settings) {
         this.scriptClassInfo = scriptClassInfo;
         this.scriptClassInfo = scriptClassInfo;
         this.settings = settings;
         this.settings = settings;
         this.sourceName = sourceName;
         this.sourceName = sourceName;
         this.source = (SClass)visit(buildAntlrTree(sourceText));
         this.source = (SClass)visit(buildAntlrTree(sourceText));
+
+        this.identifier = 0;
+    }
+
+    private int nextIdentifier() {
+        return identifier++;
     }
     }
 
 
     private SourceContext buildAntlrTree(String source) {
     private SourceContext buildAntlrTree(String source) {
@@ -248,7 +256,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             String name = method.getName().substring(3);
             String name = method.getName().substring(3);
             name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
             name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
 
 
-            statements.add(new SDeclaration(location(ctx),
+            statements.add(new SDeclaration(nextIdentifier(), location(ctx),
                     new DResolvedType(location(ctx), scriptClassInfo.getGetReturns().get(index), false), name, false, null));
                     new DResolvedType(location(ctx), scriptClassInfo.getGetReturns().get(index), false), name, false, null));
         }
         }
 
 
@@ -266,11 +274,11 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         }
         }
 
 
         // generate the execute method from the collected statements and parameters
         // generate the execute method from the collected statements and parameters
-        SFunction execute = new SFunction(location(ctx), returnCanonicalTypeName, "execute", paramTypes, paramNames, new SBlock(
-                location(ctx), statements), true, false, false, true);
+        SFunction execute = new SFunction(nextIdentifier(), location(ctx), returnCanonicalTypeName, "execute", paramTypes, paramNames,
+                new SBlock(nextIdentifier(), location(ctx), statements), true, false, false, true);
         functions.add(execute);
         functions.add(execute);
 
 
-        return new SClass(location(ctx), functions);
+        return new SClass(nextIdentifier(), location(ctx), functions);
     }
     }
 
 
     @Override
     @Override
@@ -297,8 +305,8 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             statements.add((AStatement)visit(ctx.block().dstatement()));
             statements.add((AStatement)visit(ctx.block().dstatement()));
         }
         }
 
 
-        return new SFunction(
-                location(ctx), rtnType, name, paramTypes, paramNames, new SBlock(location(ctx), statements), false, true, false, false);
+        return new SFunction(nextIdentifier(), location(ctx),
+                rtnType, name, paramTypes, paramNames, new SBlock(nextIdentifier(), location(ctx), statements), false, true, false, false);
     }
     }
 
 
     @Override
     @Override
@@ -325,9 +333,9 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         if (ctx.trailer().size() > 1) {
         if (ctx.trailer().size() > 1) {
             SBlock elseblock = (SBlock)visit(ctx.trailer(1));
             SBlock elseblock = (SBlock)visit(ctx.trailer(1));
 
 
-            return new SIfElse(location(ctx), expression, ifblock, elseblock);
+            return new SIfElse(nextIdentifier(), location(ctx), expression, ifblock, elseblock);
         } else {
         } else {
-            return new SIf(location(ctx), expression, ifblock);
+            return new SIf(nextIdentifier(), location(ctx), expression, ifblock);
         }
         }
     }
     }
 
 
@@ -338,9 +346,9 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         if (ctx.trailer() != null) {
         if (ctx.trailer() != null) {
             SBlock block = (SBlock)visit(ctx.trailer());
             SBlock block = (SBlock)visit(ctx.trailer());
 
 
-            return new SWhile(location(ctx), expression, block);
+            return new SWhile(nextIdentifier(), location(ctx), expression, block);
         } else if (ctx.empty() != null) {
         } else if (ctx.empty() != null) {
-            return new SWhile(location(ctx), expression, null);
+            return new SWhile(nextIdentifier(), location(ctx), expression, null);
         } else {
         } else {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
@@ -351,7 +359,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         AExpression expression = (AExpression)visit(ctx.expression());
         AExpression expression = (AExpression)visit(ctx.expression());
         SBlock block = (SBlock)visit(ctx.block());
         SBlock block = (SBlock)visit(ctx.block());
 
 
-        return new SDo(location(ctx), block, expression);
+        return new SDo(nextIdentifier(), location(ctx), expression, block);
     }
     }
 
 
     @Override
     @Override
@@ -363,9 +371,9 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         if (ctx.trailer() != null) {
         if (ctx.trailer() != null) {
             SBlock block = (SBlock)visit(ctx.trailer());
             SBlock block = (SBlock)visit(ctx.trailer());
 
 
-            return new SFor(location(ctx), initializer, expression, afterthought, block);
+            return new SFor(nextIdentifier(), location(ctx), initializer, expression, afterthought, block);
         } else if (ctx.empty() != null) {
         } else if (ctx.empty() != null) {
-            return new SFor(location(ctx), initializer, expression, afterthought, null);
+            return new SFor(nextIdentifier(), location(ctx), initializer, expression, afterthought, null);
         } else {
         } else {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
@@ -378,7 +386,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         AExpression expression = (AExpression)visit(ctx.expression());
         AExpression expression = (AExpression)visit(ctx.expression());
         SBlock block = (SBlock)visit(ctx.trailer());
         SBlock block = (SBlock)visit(ctx.trailer());
 
 
-        return new SEach(location(ctx), type, name, expression, block);
+        return new SEach(nextIdentifier(), location(ctx), type, name, expression, block);
     }
     }
 
 
     @Override
     @Override
@@ -387,7 +395,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         AExpression expression = (AExpression)visit(ctx.expression());
         AExpression expression = (AExpression)visit(ctx.expression());
         SBlock block = (SBlock)visit(ctx.trailer());
         SBlock block = (SBlock)visit(ctx.trailer());
 
 
-        return new SEach(location(ctx), "def", name, expression, block);
+        return new SEach(nextIdentifier(), location(ctx), "def", name, expression, block);
     }
     }
 
 
     @Override
     @Override
@@ -397,12 +405,12 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
 
 
     @Override
     @Override
     public ANode visitContinue(ContinueContext ctx) {
     public ANode visitContinue(ContinueContext ctx) {
-        return new SContinue(location(ctx));
+        return new SContinue(nextIdentifier(), location(ctx));
     }
     }
 
 
     @Override
     @Override
     public ANode visitBreak(BreakContext ctx) {
     public ANode visitBreak(BreakContext ctx) {
-        return new SBreak(location(ctx));
+        return new SBreak(nextIdentifier(), location(ctx));
     }
     }
 
 
     @Override
     @Override
@@ -413,7 +421,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             expression = (AExpression) visit(ctx.expression());
             expression = (AExpression) visit(ctx.expression());
         }
         }
 
 
-        return new SReturn(location(ctx), expression);
+        return new SReturn(nextIdentifier(), location(ctx), expression);
     }
     }
 
 
     @Override
     @Override
@@ -425,21 +433,21 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             catches.add((SCatch)visit(trap));
             catches.add((SCatch)visit(trap));
         }
         }
 
 
-        return new STry(location(ctx), block, catches);
+        return new STry(nextIdentifier(), location(ctx), block, catches);
     }
     }
 
 
     @Override
     @Override
     public ANode visitThrow(ThrowContext ctx) {
     public ANode visitThrow(ThrowContext ctx) {
         AExpression expression = (AExpression)visit(ctx.expression());
         AExpression expression = (AExpression)visit(ctx.expression());
 
 
-        return new SThrow(location(ctx), expression);
+        return new SThrow(nextIdentifier(), location(ctx), expression);
     }
     }
 
 
     @Override
     @Override
     public ANode visitExpr(ExprContext ctx) {
     public ANode visitExpr(ExprContext ctx) {
         AExpression expression = (AExpression)visit(ctx.expression());
         AExpression expression = (AExpression)visit(ctx.expression());
 
 
-        return new SExpression(location(ctx), expression);
+        return new SExpression(nextIdentifier(), location(ctx), expression);
     }
     }
 
 
     @Override
     @Override
@@ -450,7 +458,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             List<AStatement> statements = new ArrayList<>();
             List<AStatement> statements = new ArrayList<>();
             statements.add((AStatement)visit(ctx.statement()));
             statements.add((AStatement)visit(ctx.statement()));
 
 
-            return new SBlock(location(ctx), statements);
+            return new SBlock(nextIdentifier(), location(ctx), statements);
         } else {
         } else {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
@@ -471,7 +479,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
                 statements.add((AStatement)visit(ctx.dstatement()));
                 statements.add((AStatement)visit(ctx.dstatement()));
             }
             }
 
 
-            return new SBlock(location(ctx), statements);
+            return new SBlock(nextIdentifier(), location(ctx), statements);
         }
         }
     }
     }
 
 
@@ -506,10 +514,10 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             AExpression expression = declvar.expression() == null ? null : (AExpression)visit(declvar.expression());
             AExpression expression = declvar.expression() == null ? null : (AExpression)visit(declvar.expression());
             DUnresolvedType unresolvedType = new DUnresolvedType(location(declvar), type);
             DUnresolvedType unresolvedType = new DUnresolvedType(location(declvar), type);
 
 
-            declarations.add(new SDeclaration(location(declvar), unresolvedType, name, true, expression));
+            declarations.add(new SDeclaration(nextIdentifier(), location(declvar), unresolvedType, name, true, expression));
         }
         }
 
 
-        return new SDeclBlock(location(ctx), declarations);
+        return new SDeclBlock(nextIdentifier(), location(ctx), declarations);
     }
     }
 
 
     @Override
     @Override
@@ -533,8 +541,9 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         String name = ctx.ID().getText();
         String name = ctx.ID().getText();
         SBlock block = (SBlock)visit(ctx.block());
         SBlock block = (SBlock)visit(ctx.block());
 
 
-        return new SCatch(location(ctx), new DResolvedType(location(ctx), Exception.class),
-                new SDeclaration(location(ctx.type()), new DUnresolvedType(location(ctx.type()), type), name, false, null), block);
+        return new SCatch(nextIdentifier(), location(ctx), Exception.class,
+                new SDeclaration(nextIdentifier(), location(ctx.type()),
+                        new DUnresolvedType(location(ctx.type()), type), name, false, null), block);
     }
     }
 
 
     @Override
     @Override
@@ -578,7 +587,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
 
 
-        return new EBinary(location(ctx), operation, left, right);
+        return new EBinary(nextIdentifier(), location(ctx), left, right, operation);
     }
     }
 
 
     @Override
     @Override
@@ -607,7 +616,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
 
 
-        return new EComp(location(ctx), operation, left, right);
+        return new EComp(nextIdentifier(), location(ctx), left, right, operation);
     }
     }
 
 
     @Override
     @Override
@@ -615,7 +624,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         AExpression expr = (AExpression)visit(ctx.noncondexpression());
         AExpression expr = (AExpression)visit(ctx.noncondexpression());
         String type = ctx.decltype().getText();
         String type = ctx.decltype().getText();
 
 
-        return new EInstanceof(location(ctx), expr, type);
+        return new EInstanceof(nextIdentifier(), location(ctx), expr, type);
     }
     }
 
 
     @Override
     @Override
@@ -632,7 +641,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
 
 
-        return new EBool(location(ctx), operation, left, right);
+        return new EBool(nextIdentifier(), location(ctx), left, right, operation);
     }
     }
 
 
     @Override
     @Override
@@ -640,7 +649,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         AExpression left = (AExpression)visit(ctx.noncondexpression(0));
         AExpression left = (AExpression)visit(ctx.noncondexpression(0));
         AExpression right = (AExpression)visit(ctx.noncondexpression(1));
         AExpression right = (AExpression)visit(ctx.noncondexpression(1));
 
 
-        return new EElvis(location(ctx), left, right);
+        return new EElvis(nextIdentifier(), location(ctx), left, right);
     }
     }
 
 
     @Override
     @Override
@@ -654,7 +663,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         AExpression left = (AExpression)visit(ctx.expression(0));
         AExpression left = (AExpression)visit(ctx.expression(0));
         AExpression right = (AExpression)visit(ctx.expression(1));
         AExpression right = (AExpression)visit(ctx.expression(1));
 
 
-        return new EConditional(location(ctx), condition, left, right);
+        return new EConditional(nextIdentifier(), location(ctx), condition, left, right);
     }
     }
 
 
     @Override
     @Override
@@ -692,7 +701,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
 
 
-        return new EAssignment(location(ctx), lhs, rhs, false, operation);
+        return new EAssignment(nextIdentifier(), location(ctx), lhs, rhs, false, operation);
     }
     }
 
 
     @Override
     @Override
@@ -709,7 +718,8 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
 
 
-        return new EAssignment(location(ctx), expression, new EConstant(location(ctx), 1), false, operation);
+        return new EAssignment(nextIdentifier(), location(ctx), expression,
+                new EConstant(nextIdentifier(), location(ctx), 1), false, operation);
     }
     }
 
 
     @Override
     @Override
@@ -726,7 +736,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
 
 
-        return new EUnary(location(ctx), operation, expression);
+        return new EUnary(nextIdentifier(), location(ctx), expression, operation);
     }
     }
 
 
     @Override
     @Override
@@ -753,7 +763,8 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
 
 
-        return new EAssignment(location(ctx), expression, new EConstant(location(ctx), 1), true, operation);
+        return new EAssignment(nextIdentifier(), location(ctx), expression,
+                new EConstant(nextIdentifier(), location(ctx), 1), true, operation);
     }
     }
 
 
     @Override
     @Override
@@ -770,7 +781,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
 
 
-        return new EUnary(location(ctx), operation, expression);
+        return new EUnary(nextIdentifier(), location(ctx), expression, operation);
     }
     }
 
 
     @Override
     @Override
@@ -783,7 +794,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         String type = ctx.primordefcasttype().getText();
         String type = ctx.primordefcasttype().getText();
         AExpression child = (AExpression)visit(ctx.unary());
         AExpression child = (AExpression)visit(ctx.unary());
 
 
-        return new EExplicit(location(ctx), new DUnresolvedType(location(ctx.primordefcasttype()), type), child);
+        return new EExplicit(nextIdentifier(), location(ctx), new DUnresolvedType(location(ctx.primordefcasttype()), type), child);
     }
     }
 
 
     @Override
     @Override
@@ -791,7 +802,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         String type = ctx.refcasttype().getText();
         String type = ctx.refcasttype().getText();
         AExpression child = (AExpression)visit(ctx.unarynotaddsub());
         AExpression child = (AExpression)visit(ctx.unarynotaddsub());
 
 
-        return new EExplicit(location(ctx), new DUnresolvedType(location(ctx.refcasttype()), type), child);
+        return new EExplicit(nextIdentifier(), location(ctx), new DUnresolvedType(location(ctx.refcasttype()), type), child);
     }
     }
 
 
     @Override
     @Override
@@ -824,13 +835,13 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
     @Override
     @Override
     public ANode visitNumeric(NumericContext ctx) {
     public ANode visitNumeric(NumericContext ctx) {
         if (ctx.DECIMAL() != null) {
         if (ctx.DECIMAL() != null) {
-            return new EDecimal(location(ctx), ctx.DECIMAL().getText());
+            return new EDecimal(nextIdentifier(), location(ctx), ctx.DECIMAL().getText());
         } else if (ctx.HEX() != null) {
         } else if (ctx.HEX() != null) {
-            return new ENumeric(location(ctx), ctx.HEX().getText().substring(2), 16);
+            return new ENumeric(nextIdentifier(), location(ctx), ctx.HEX().getText().substring(2), 16);
         } else if (ctx.INTEGER() != null) {
         } else if (ctx.INTEGER() != null) {
-            return new ENumeric(location(ctx), ctx.INTEGER().getText(), 10);
+            return new ENumeric(nextIdentifier(), location(ctx), ctx.INTEGER().getText(), 10);
         } else if (ctx.OCTAL() != null) {
         } else if (ctx.OCTAL() != null) {
-            return new ENumeric(location(ctx), ctx.OCTAL().getText().substring(1), 8);
+            return new ENumeric(nextIdentifier(), location(ctx), ctx.OCTAL().getText().substring(1), 8);
         } else {
         } else {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
@@ -838,17 +849,17 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
 
 
     @Override
     @Override
     public ANode visitTrue(TrueContext ctx) {
     public ANode visitTrue(TrueContext ctx) {
-        return new EBoolean(location(ctx), true);
+        return new EBoolean(nextIdentifier(), location(ctx), true);
     }
     }
 
 
     @Override
     @Override
     public ANode visitFalse(FalseContext ctx) {
     public ANode visitFalse(FalseContext ctx) {
-        return new EBoolean(location(ctx), false);
+        return new EBoolean(nextIdentifier(), location(ctx), false);
     }
     }
 
 
     @Override
     @Override
     public ANode visitNull(NullContext ctx) {
     public ANode visitNull(NullContext ctx) {
-        return new ENull(location(ctx));
+        return new ENull(nextIdentifier(), location(ctx));
     }
     }
 
 
     @Override
     @Override
@@ -873,7 +884,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         }
         }
         string.setLength(dest);
         string.setLength(dest);
 
 
-        return new EString(location(ctx), string.toString());
+        return new EString(nextIdentifier(), location(ctx), string.toString());
     }
     }
 
 
     @Override
     @Override
@@ -883,7 +894,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         String pattern = text.substring(1, lastSlash);
         String pattern = text.substring(1, lastSlash);
         String flags = text.substring(lastSlash + 1);
         String flags = text.substring(lastSlash + 1);
 
 
-        return new ERegex(location(ctx), pattern, flags);
+        return new ERegex(nextIdentifier(), location(ctx), pattern, flags);
     }
     }
 
 
     @Override
     @Override
@@ -900,7 +911,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
     public ANode visitVariable(VariableContext ctx) {
     public ANode visitVariable(VariableContext ctx) {
         String name = ctx.ID().getText();
         String name = ctx.ID().getText();
 
 
-        return new EVariable(location(ctx), name);
+        return new ESymbol(nextIdentifier(), location(ctx), name);
     }
     }
 
 
     @Override
     @Override
@@ -908,7 +919,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         String name = ctx.ID().getText();
         String name = ctx.ID().getText();
         List<AExpression> arguments = collectArguments(ctx.arguments());
         List<AExpression> arguments = collectArguments(ctx.arguments());
 
 
-        return new ECallLocal(location(ctx), name, arguments);
+        return new ECallLocal(nextIdentifier(), location(ctx), name, arguments);
     }
     }
 
 
     @Override
     @Override
@@ -916,7 +927,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         String type = ctx.type().getText();
         String type = ctx.type().getText();
         List<AExpression> arguments = collectArguments(ctx.arguments());
         List<AExpression> arguments = collectArguments(ctx.arguments());
 
 
-        return new ENewObj(location(ctx), type, arguments);
+        return new ENewObj(nextIdentifier(), location(ctx), type, arguments);
     }
     }
 
 
     private AExpression buildPostfixChain(AExpression primary, PostdotContext postdot, List<PostfixContext> postfixes) {
     private AExpression buildPostfixChain(AExpression primary, PostdotContext postdot, List<PostfixContext> postfixes) {
@@ -974,7 +985,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         String name = ctx.DOTID().getText();
         String name = ctx.DOTID().getText();
         List<AExpression> arguments = collectArguments(ctx.arguments());
         List<AExpression> arguments = collectArguments(ctx.arguments());
 
 
-        return new ECall(location(ctx), prefix, name, arguments, ctx.NSDOT() != null);
+        return new ECall(nextIdentifier(), location(ctx), prefix, name, arguments, ctx.NSDOT() != null);
     }
     }
 
 
     @Override
     @Override
@@ -993,7 +1004,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
             throw location(ctx).createError(new IllegalStateException("illegal tree structure"));
         }
         }
 
 
-        return new EDot(location(ctx), prefix, ctx.NSDOT() != null, value);
+        return new EDot(nextIdentifier(), location(ctx), prefix, value, ctx.NSDOT() != null);
     }
     }
 
 
     @Override
     @Override
@@ -1004,7 +1015,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
     public AExpression visitBraceaccess(BraceaccessContext ctx, AExpression prefix) {
     public AExpression visitBraceaccess(BraceaccessContext ctx, AExpression prefix) {
         AExpression expression = (AExpression)visit(ctx.expression());
         AExpression expression = (AExpression)visit(ctx.expression());
 
 
-        return new EBrace(location(ctx), prefix, expression);
+        return new EBrace(nextIdentifier(), location(ctx), prefix, expression);
     }
     }
 
 
     @Override
     @Override
@@ -1017,7 +1028,8 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             expressions.add((AExpression)visit(expression));
             expressions.add((AExpression)visit(expression));
         }
         }
 
 
-        return buildPostfixChain(new ENewArray(location(ctx), type.toString(), expressions, false), ctx.postdot(), ctx.postfix());
+        return buildPostfixChain(
+                new ENewArray(nextIdentifier(), location(ctx), type.toString(), expressions, false), ctx.postdot(), ctx.postfix());
     }
     }
 
 
     @Override
     @Override
@@ -1029,7 +1041,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             expressions.add((AExpression)visit(expression));
             expressions.add((AExpression)visit(expression));
         }
         }
 
 
-        return buildPostfixChain(new ENewArray(location(ctx), type, expressions, true), null, ctx.postfix());
+        return buildPostfixChain(new ENewArray(nextIdentifier(), location(ctx), type, expressions, true), null, ctx.postfix());
     }
     }
 
 
     @Override
     @Override
@@ -1040,7 +1052,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             values.add((AExpression)visit(expression));
             values.add((AExpression)visit(expression));
         }
         }
 
 
-        return new EListInit(location(ctx), values);
+        return new EListInit(nextIdentifier(), location(ctx), values);
     }
     }
 
 
     @Override
     @Override
@@ -1053,7 +1065,7 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
             values.add((AExpression)visit(maptoken.expression(1)));
             values.add((AExpression)visit(maptoken.expression(1)));
         }
         }
 
 
-        return new EMapInit(location(ctx), keys, values);
+        return new EMapInit(nextIdentifier(), location(ctx), keys, values);
     }
     }
 
 
     @Override
     @Override
@@ -1108,13 +1120,13 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
         if (ctx.expression() != null) {
         if (ctx.expression() != null) {
             // single expression
             // single expression
             AExpression expression = (AExpression)visit(ctx.expression());
             AExpression expression = (AExpression)visit(ctx.expression());
-            block = new SBlock(location(ctx),
-                    Collections.singletonList(new SReturn(location(ctx), expression)));
+            block = new SBlock(nextIdentifier(), location(ctx),
+                    Collections.singletonList(new SReturn(nextIdentifier(), location(ctx), expression)));
         } else {
         } else {
             block = (SBlock)visit(ctx.block());
             block = (SBlock)visit(ctx.block());
         }
         }
 
 
-        return new ELambda(location(ctx), paramTypes, paramNames, block);
+        return new ELambda(nextIdentifier(), location(ctx), paramTypes, paramNames, block);
     }
     }
 
 
     @Override
     @Override
@@ -1124,18 +1136,18 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
 
 
     @Override
     @Override
     public ANode visitClassfuncref(ClassfuncrefContext ctx) {
     public ANode visitClassfuncref(ClassfuncrefContext ctx) {
-        return new EFunctionRef(location(ctx), ctx.decltype().getText(), ctx.ID().getText());
+        return new EFunctionRef(nextIdentifier(), location(ctx), ctx.decltype().getText(), ctx.ID().getText());
     }
     }
 
 
     @Override
     @Override
     public ANode visitConstructorfuncref(ConstructorfuncrefContext ctx) {
     public ANode visitConstructorfuncref(ConstructorfuncrefContext ctx) {
         return ctx.decltype().LBRACE().isEmpty() ?
         return ctx.decltype().LBRACE().isEmpty() ?
-                new EFunctionRef(location(ctx), ctx.decltype().getText(), ctx.NEW().getText()) :
-                new ENewArrayFunctionRef(location(ctx), ctx.decltype().getText());
+                new EFunctionRef(nextIdentifier(), location(ctx), ctx.decltype().getText(), ctx.NEW().getText()) :
+                new ENewArrayFunctionRef(nextIdentifier(), location(ctx), ctx.decltype().getText());
     }
     }
 
 
     @Override
     @Override
     public ANode visitLocalfuncref(LocalfuncrefContext ctx) {
     public ANode visitLocalfuncref(LocalfuncrefContext ctx) {
-        return new EFunctionRef(location(ctx), ctx.THIS().getText(), ctx.ID().getText());
+        return new EFunctionRef(nextIdentifier(), location(ctx), ctx.THIS().getText(), ctx.ID().getText());
     }
     }
 }
 }

+ 2 - 13
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AExpression.java

@@ -106,19 +106,8 @@ public abstract class AExpression extends ANode {
     /**
     /**
      * Standard constructor with location used for error tracking.
      * Standard constructor with location used for error tracking.
      */
      */
-    AExpression(Location location) {
-        super(location);
-    }
-
-    /**
-     * Replaces standard instanceof to ignore precedence within the tree.
-     */
-    AExpression getChildIf(Class<? extends AExpression> type) {
-        if (type.isAssignableFrom(getClass())) {
-            return this;
-        }
-
-        return null;
+    AExpression(int indentifier, Location location) {
+        super(indentifier, location);
     }
     }
 
 
     /**
     /**

+ 21 - 6
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ANode.java

@@ -28,22 +28,37 @@ import java.util.Objects;
  */
  */
 public abstract class ANode {
 public abstract class ANode {
 
 
-    /**
-     * The identifier of the script and character offset used for debugging and errors.
-     */
-    protected final Location location;
+    private final int identifier;
+    private final Location location;
 
 
     /**
     /**
      * Standard constructor with location used for error tracking.
      * Standard constructor with location used for error tracking.
      */
      */
-    ANode(Location location) {
+    public ANode(int identifier, Location location) {
+        this.identifier = identifier;
         this.location = Objects.requireNonNull(location);
         this.location = Objects.requireNonNull(location);
     }
     }
 
 
+    /**
+     * An unique id for the node. This is guaranteed to be smallest possible
+     * value so it can be used for value caching in an array instead of a
+     * hash look up.
+     */
+    public int getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * The identifier of the script and character offset used for debugging and errors.
+     */
+    public Location getLocation() {
+        return location;
+    }
+
     /**
     /**
      * Create an error with location information pointing to this node.
      * Create an error with location information pointing to this node.
      */
      */
-    RuntimeException createError(RuntimeException exception) {
+    public RuntimeException createError(RuntimeException exception) {
         return location.createError(exception);
         return location.createError(exception);
     }
     }
 }
 }

+ 2 - 2
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AStatement.java

@@ -105,8 +105,8 @@ public abstract class AStatement extends ANode {
     /**
     /**
      * Standard constructor with location used for error tracking.
      * Standard constructor with location used for error tracking.
      */
      */
-    AStatement(Location location) {
-        super(location);
+    AStatement(int identifier, Location location) {
+        super(identifier, location);
     }
     }
 
 
     /**
     /**

+ 38 - 26
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EAssignment.java

@@ -43,26 +43,42 @@ import java.util.Objects;
  */
  */
 public class EAssignment extends AExpression {
 public class EAssignment extends AExpression {
 
 
-    protected final AExpression lhs;
-    protected final AExpression rhs;
-    protected final boolean post;
-    protected final Operation operation;
+    private final AExpression leftNode;
+    private final AExpression rightNode;
+    private final boolean postIfRead;
+    private final Operation operation;
 
 
-    public EAssignment(Location location, AExpression lhs, AExpression rhs, boolean post, Operation operation) {
-        super(location);
+    public EAssignment(int identifier, Location location,
+            AExpression leftNode, AExpression rightNode, boolean postIfRead, Operation operation) {
 
 
-        this.lhs = Objects.requireNonNull(lhs);
-        this.rhs = Objects.requireNonNull(rhs);
-        this.post = post;
+        super(identifier, location);
+
+        this.leftNode = Objects.requireNonNull(leftNode);
+        this.rightNode = Objects.requireNonNull(rightNode);
+        this.postIfRead = postIfRead;
         this.operation = operation;
         this.operation = operation;
     }
     }
 
 
+    public AExpression getLeftNode() {
+        return leftNode;
+    }
+
+    public AExpression getRightNode() {
+        return rightNode;
+    }
+
+    public boolean postIfRead() {
+        return postIfRead;
+    }
+
+    public Operation getOperation() {
+        return operation;
+    }
+
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         Output output = new Output();
         Output output = new Output();
 
 
-        AExpression rhs = this.rhs;
-        Operation operation = this.operation;
         boolean cat = false;
         boolean cat = false;
         Class<?> promote = null;
         Class<?> promote = null;
         Class<?> shiftDistance = null;
         Class<?> shiftDistance = null;
@@ -73,13 +89,13 @@ public class EAssignment extends AExpression {
         Input leftInput = new Input();
         Input leftInput = new Input();
         leftInput.read = input.read;
         leftInput.read = input.read;
         leftInput.write = true;
         leftInput.write = true;
-        Output leftOutput = analyze(lhs, classNode, scriptRoot, scope, leftInput);
+        Output leftOutput = analyze(leftNode, classNode, scriptRoot, scope, leftInput);
 
 
         Input rightInput = new Input();
         Input rightInput = new Input();
         Output rightOutput;
         Output rightOutput;
 
 
         if (operation != null) {
         if (operation != null) {
-            rightOutput = analyze(rhs, classNode, scriptRoot, scope, rightInput);
+            rightOutput = analyze(rightNode, classNode, scriptRoot, scope, rightInput);
             boolean shift = false;
             boolean shift = false;
 
 
             if (operation == Operation.MUL) {
             if (operation == Operation.MUL) {
@@ -143,17 +159,15 @@ public class EAssignment extends AExpression {
                 rightInput.expected = promote;
                 rightInput.expected = promote;
             }
             }
 
 
-            rightCast = AnalyzerCaster.getLegalCast(rhs.location,
+            rightCast = AnalyzerCaster.getLegalCast(rightNode.getLocation(),
                     rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
                     rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
 
 
-            there = AnalyzerCaster.getLegalCast(location, leftOutput.actual, promote, false, false);
-            back = AnalyzerCaster.getLegalCast(location, promote, leftOutput.actual, true, false);
-
-
-        } else if (rhs != null) {
+            there = AnalyzerCaster.getLegalCast(getLocation(), leftOutput.actual, promote, false, false);
+            back = AnalyzerCaster.getLegalCast(getLocation(), promote, leftOutput.actual, true, false);
+        } else {
             // If the lhs node is a def optimized node we update the actual type to remove the need for a cast.
             // If the lhs node is a def optimized node we update the actual type to remove the need for a cast.
             if (leftOutput.isDefOptimized) {
             if (leftOutput.isDefOptimized) {
-                rightOutput = analyze(rhs, classNode, scriptRoot, scope, rightInput);
+                rightOutput = analyze(rightNode, classNode, scriptRoot, scope, rightInput);
 
 
                 if (rightOutput.actual == void.class) {
                 if (rightOutput.actual == void.class) {
                     throw createError(new IllegalArgumentException("Right-hand side cannot be a [void] type for assignment."));
                     throw createError(new IllegalArgumentException("Right-hand side cannot be a [void] type for assignment."));
@@ -173,13 +187,11 @@ public class EAssignment extends AExpression {
             // Otherwise, we must adapt the rhs type to the lhs type with a cast.
             // Otherwise, we must adapt the rhs type to the lhs type with a cast.
             } else {
             } else {
                 rightInput.expected = leftOutput.actual;
                 rightInput.expected = leftOutput.actual;
-                rightOutput = analyze(rhs, classNode, scriptRoot, scope, rightInput);
+                rightOutput = analyze(rightNode, classNode, scriptRoot, scope, rightInput);
             }
             }
 
 
-            rightCast = AnalyzerCaster.getLegalCast(rhs.location,
+            rightCast = AnalyzerCaster.getLegalCast(rightNode.getLocation(),
                     rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
                     rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
-        } else {
-            throw new IllegalStateException("Illegal tree structure.");
         }
         }
 
 
         output.actual = input.read ? leftOutput.actual : void.class;
         output.actual = input.read ? leftOutput.actual : void.class;
@@ -189,10 +201,10 @@ public class EAssignment extends AExpression {
         assignmentNode.setLeftNode(leftOutput.expressionNode);
         assignmentNode.setLeftNode(leftOutput.expressionNode);
         assignmentNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
         assignmentNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
 
 
-        assignmentNode.setLocation(location);
+        assignmentNode.setLocation(getLocation());
         assignmentNode.setExpressionType(output.actual);
         assignmentNode.setExpressionType(output.actual);
         assignmentNode.setCompoundType(promote);
         assignmentNode.setCompoundType(promote);
-        assignmentNode.setPost(post);
+        assignmentNode.setPost(postIfRead);
         assignmentNode.setOperation(operation);
         assignmentNode.setOperation(operation);
         assignmentNode.setRead(input.read);
         assignmentNode.setRead(input.read);
         assignmentNode.setCat(cat);
         assignmentNode.setCat(cat);

+ 24 - 12
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java

@@ -38,16 +38,28 @@ import java.util.regex.Pattern;
  */
  */
 public class EBinary extends AExpression {
 public class EBinary extends AExpression {
 
 
-    protected final Operation operation;
-    protected final AExpression left;
-    protected final AExpression right;
+    private final AExpression leftNode;
+    private final AExpression rightNode;
+    private final Operation operation;
 
 
-    public EBinary(Location location, Operation operation, AExpression left, AExpression right) {
-        super(location);
+    public EBinary(int identifier, Location location, AExpression leftNode, AExpression rightNode, Operation operation) {
+        super(identifier, location);
 
 
         this.operation = Objects.requireNonNull(operation);
         this.operation = Objects.requireNonNull(operation);
-        this.left = Objects.requireNonNull(left);
-        this.right = Objects.requireNonNull(right);
+        this.leftNode = Objects.requireNonNull(leftNode);
+        this.rightNode = Objects.requireNonNull(rightNode);
+    }
+
+    public AExpression getLeftNode() {
+        return leftNode;
+    }
+
+    public AExpression getRightNode() {
+        return rightNode;
+    }
+
+    public Operation getOperation() {
+        return operation;
     }
     }
 
 
     @Override
     @Override
@@ -67,11 +79,11 @@ public class EBinary extends AExpression {
         boolean originallyExplicit = input.explicit; // record whether there was originally an explicit cast
         boolean originallyExplicit = input.explicit; // record whether there was originally an explicit cast
 
 
         Input leftInput = new Input();
         Input leftInput = new Input();
-        Output leftOutput = analyze(left, classNode, scriptRoot, scope, leftInput);
+        Output leftOutput = analyze(leftNode, classNode, scriptRoot, scope, leftInput);
 
 
         Output output = new Output();
         Output output = new Output();
         Input rightInput = new Input();
         Input rightInput = new Input();
-        Output rightOutput = analyze(right, classNode, scriptRoot, scope, rightInput);
+        Output rightOutput = analyze(rightNode, classNode, scriptRoot, scope, rightInput);
 
 
         if (operation == Operation.FIND || operation == Operation.MATCH) {
         if (operation == Operation.FIND || operation == Operation.MATCH) {
             leftInput.expected = String.class;
             leftInput.expected = String.class;
@@ -151,9 +163,9 @@ public class EBinary extends AExpression {
             }
             }
         }
         }
 
 
-        PainlessCast leftCast = AnalyzerCaster.getLegalCast(left.location,
+        PainlessCast leftCast = AnalyzerCaster.getLegalCast(leftNode.getLocation(),
                 leftOutput.actual, leftInput.expected, leftInput.explicit, leftInput.internal);
                 leftOutput.actual, leftInput.expected, leftInput.explicit, leftInput.internal);
-        PainlessCast rightCast = AnalyzerCaster.getLegalCast(right.location,
+        PainlessCast rightCast = AnalyzerCaster.getLegalCast(rightNode.getLocation(),
                 rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
                 rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
 
 
         BinaryMathNode binaryMathNode = new BinaryMathNode();
         BinaryMathNode binaryMathNode = new BinaryMathNode();
@@ -161,7 +173,7 @@ public class EBinary extends AExpression {
         binaryMathNode.setLeftNode(cast(leftOutput.expressionNode, leftCast));
         binaryMathNode.setLeftNode(cast(leftOutput.expressionNode, leftCast));
         binaryMathNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
         binaryMathNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
 
 
-        binaryMathNode.setLocation(location);
+        binaryMathNode.setLocation(getLocation());
         binaryMathNode.setExpressionType(output.actual);
         binaryMathNode.setExpressionType(output.actual);
         binaryMathNode.setBinaryType(promote);
         binaryMathNode.setBinaryType(promote);
         binaryMathNode.setShiftType(shiftDistance);
         binaryMathNode.setShiftType(shiftDistance);

+ 24 - 12
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBool.java

@@ -35,16 +35,28 @@ import java.util.Objects;
  */
  */
 public class EBool extends AExpression {
 public class EBool extends AExpression {
 
 
-    protected final Operation operation;
-    protected final AExpression left;
-    protected final AExpression right;
+    private final AExpression leftNode;
+    private final AExpression rightNode;
+    private final Operation operation;
 
 
-    public EBool(Location location, Operation operation, AExpression left, AExpression right) {
-        super(location);
+    public EBool(int identifier, Location location, AExpression leftNode, AExpression rightNode, Operation operation) {
+        super(identifier, location);
 
 
         this.operation = Objects.requireNonNull(operation);
         this.operation = Objects.requireNonNull(operation);
-        this.left = Objects.requireNonNull(left);
-        this.right = Objects.requireNonNull(right);
+        this.leftNode = Objects.requireNonNull(leftNode);
+        this.rightNode = Objects.requireNonNull(rightNode);
+    }
+
+    public AExpression getLeftNode() {
+        return leftNode;
+    }
+
+    public AExpression getRightNode() {
+        return rightNode;
+    }
+
+    public Operation getOperation() {
+        return operation;
     }
     }
 
 
     @Override
     @Override
@@ -63,14 +75,14 @@ public class EBool extends AExpression {
 
 
         Input leftInput = new Input();
         Input leftInput = new Input();
         leftInput.expected = boolean.class;
         leftInput.expected = boolean.class;
-        Output leftOutput = analyze(left, classNode, scriptRoot, scope, leftInput);
-        PainlessCast leftCast = AnalyzerCaster.getLegalCast(left.location,
+        Output leftOutput = analyze(leftNode, classNode, scriptRoot, scope, leftInput);
+        PainlessCast leftCast = AnalyzerCaster.getLegalCast(leftNode.getLocation(),
                 leftOutput.actual, leftInput.expected, leftInput.explicit, leftInput.internal);
                 leftOutput.actual, leftInput.expected, leftInput.explicit, leftInput.internal);
 
 
         Input rightInput = new Input();
         Input rightInput = new Input();
         rightInput.expected = boolean.class;
         rightInput.expected = boolean.class;
-        Output rightOutput = analyze(right, classNode, scriptRoot, scope, rightInput);
-        PainlessCast rightCast = AnalyzerCaster.getLegalCast(right.location,
+        Output rightOutput = analyze(rightNode, classNode, scriptRoot, scope, rightInput);
+        PainlessCast rightCast = AnalyzerCaster.getLegalCast(rightNode.getLocation(),
                 rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
                 rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
 
 
         output.actual = boolean.class;
         output.actual = boolean.class;
@@ -80,7 +92,7 @@ public class EBool extends AExpression {
         booleanNode.setLeftNode(cast(leftOutput.expressionNode, leftCast));
         booleanNode.setLeftNode(cast(leftOutput.expressionNode, leftCast));
         booleanNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
         booleanNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
 
 
-        booleanNode.setLocation(location);
+        booleanNode.setLocation(getLocation());
         booleanNode.setExpressionType(output.actual);
         booleanNode.setExpressionType(output.actual);
         booleanNode.setOperation(operation);
         booleanNode.setOperation(operation);
 
 

+ 12 - 8
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBoolean.java

@@ -30,23 +30,27 @@ import org.elasticsearch.painless.symbol.ScriptRoot;
  */
  */
 public class EBoolean extends AExpression {
 public class EBoolean extends AExpression {
 
 
-    protected boolean constant;
+    private final boolean bool;
 
 
-    public EBoolean(Location location, boolean constant) {
-        super(location);
+    public EBoolean(int identifier, Location location, boolean bool) {
+        super(identifier, location);
 
 
-        this.constant = constant;
+        this.bool = bool;
+    }
+
+    public boolean getBool() {
+        return bool;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         if (input.write) {
         if (input.write) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "invalid assignment: cannot assign a value to boolean constant [" + constant + "]"));
+                    "invalid assignment: cannot assign a value to boolean constant [" + bool + "]"));
         }
         }
 
 
         if (input.read == false) {
         if (input.read == false) {
-            throw createError(new IllegalArgumentException("not a statement: boolean constant [" + constant + "] not used"));
+            throw createError(new IllegalArgumentException("not a statement: boolean constant [" + bool + "] not used"));
         }
         }
 
 
         Output output = new Output();
         Output output = new Output();
@@ -54,9 +58,9 @@ public class EBoolean extends AExpression {
         output.actual = boolean.class;
         output.actual = boolean.class;
 
 
         ConstantNode constantNode = new ConstantNode();
         ConstantNode constantNode = new ConstantNode();
-        constantNode.setLocation(location);
+        constantNode.setLocation(getLocation());
         constantNode.setExpressionType(output.actual);
         constantNode.setExpressionType(output.actual);
-        constantNode.setConstant(constant);
+        constantNode.setConstant(bool);
 
 
         output.expressionNode = constantNode;
         output.expressionNode = constantNode;
 
 

+ 27 - 19
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBrace.java

@@ -45,14 +45,22 @@ import java.util.Objects;
  */
  */
 public class EBrace extends AExpression {
 public class EBrace extends AExpression {
 
 
-    protected final AExpression prefix;
-    protected final AExpression index;
+    private final AExpression prefixNode;
+    private final AExpression indexNode;
 
 
-    public EBrace(Location location, AExpression prefix, AExpression index) {
-        super(location);
+    public EBrace(int identifier, Location location, AExpression prefixNode, AExpression indexNode) {
+        super(identifier, location);
 
 
-        this.prefix = Objects.requireNonNull(prefix);
-        this.index = Objects.requireNonNull(index);
+        this.prefixNode = Objects.requireNonNull(prefixNode);
+        this.indexNode = Objects.requireNonNull(indexNode);
+    }
+
+    public AExpression getPrefixNode() {
+        return prefixNode;
+    }
+
+    public AExpression getIndexNode() {
+        return indexNode;
     }
     }
 
 
     @Override
     @Override
@@ -61,7 +69,7 @@ public class EBrace extends AExpression {
             throw createError(new IllegalArgumentException("not a statement: result of brace operator not used"));
             throw createError(new IllegalArgumentException("not a statement: result of brace operator not used"));
         }
         }
 
 
-        Output prefixOutput = analyze(prefix, classNode, scriptRoot, scope, new Input());
+        Output prefixOutput = analyze(prefixNode, classNode, scriptRoot, scope, new Input());
 
 
         ExpressionNode expressionNode;
         ExpressionNode expressionNode;
         Output output = new Output();
         Output output = new Output();
@@ -69,20 +77,20 @@ public class EBrace extends AExpression {
         if (prefixOutput.actual.isArray()) {
         if (prefixOutput.actual.isArray()) {
             Input indexInput = new Input();
             Input indexInput = new Input();
             indexInput.expected = int.class;
             indexInput.expected = int.class;
-            Output indexOutput = analyze(index, classNode, scriptRoot, scope, indexInput);
-            PainlessCast indexCast = AnalyzerCaster.getLegalCast(index.location,
+            Output indexOutput = analyze(indexNode, classNode, scriptRoot, scope, indexInput);
+            PainlessCast indexCast = AnalyzerCaster.getLegalCast(indexNode.getLocation(),
                     indexOutput.actual, indexInput.expected, indexInput.explicit, indexInput.internal);
                     indexOutput.actual, indexInput.expected, indexInput.explicit, indexInput.internal);
 
 
             output.actual = prefixOutput.actual.getComponentType();
             output.actual = prefixOutput.actual.getComponentType();
 
 
             BraceSubNode braceSubNode = new BraceSubNode();
             BraceSubNode braceSubNode = new BraceSubNode();
             braceSubNode.setChildNode(cast(indexOutput.expressionNode, indexCast));
             braceSubNode.setChildNode(cast(indexOutput.expressionNode, indexCast));
-            braceSubNode.setLocation(location);
+            braceSubNode.setLocation(getLocation());
             braceSubNode.setExpressionType(output.actual);
             braceSubNode.setExpressionType(output.actual);
             expressionNode = braceSubNode;
             expressionNode = braceSubNode;
         } else if (prefixOutput.actual == def.class) {
         } else if (prefixOutput.actual == def.class) {
             Input indexInput = new Input();
             Input indexInput = new Input();
-            Output indexOutput = analyze(index, classNode, scriptRoot, scope, indexInput);
+            Output indexOutput = analyze(indexNode, classNode, scriptRoot, scope, indexInput);
 
 
             // TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
             // TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
             output.actual = input.expected == null || input.expected == ZonedDateTime.class || input.explicit ? def.class : input.expected;
             output.actual = input.expected == null || input.expected == ZonedDateTime.class || input.explicit ? def.class : input.expected;
@@ -90,7 +98,7 @@ public class EBrace extends AExpression {
 
 
             BraceSubDefNode braceSubDefNode = new BraceSubDefNode();
             BraceSubDefNode braceSubDefNode = new BraceSubDefNode();
             braceSubDefNode.setChildNode(indexOutput.expressionNode);
             braceSubDefNode.setChildNode(indexOutput.expressionNode);
-            braceSubDefNode.setLocation(location);
+            braceSubDefNode.setLocation(getLocation());
             braceSubDefNode.setExpressionType(output.actual);
             braceSubDefNode.setExpressionType(output.actual);
             expressionNode = braceSubDefNode;
             expressionNode = braceSubDefNode;
         } else if (Map.class.isAssignableFrom(prefixOutput.actual)) {
         } else if (Map.class.isAssignableFrom(prefixOutput.actual)) {
@@ -119,8 +127,8 @@ public class EBrace extends AExpression {
             if ((input.read == false || getter != null) && (input.write == false || setter != null)) {
             if ((input.read == false || getter != null) && (input.write == false || setter != null)) {
                 Input indexInput = new Input();
                 Input indexInput = new Input();
                 indexInput.expected = setter != null ? setter.typeParameters.get(0) : getter.typeParameters.get(0);
                 indexInput.expected = setter != null ? setter.typeParameters.get(0) : getter.typeParameters.get(0);
-                indexOutput = analyze(index, classNode, scriptRoot, scope, indexInput);
-                indexCast = AnalyzerCaster.getLegalCast(index.location,
+                indexOutput = analyze(indexNode, classNode, scriptRoot, scope, indexInput);
+                indexCast = AnalyzerCaster.getLegalCast(indexNode.getLocation(),
                         indexOutput.actual, indexInput.expected, indexInput.explicit, indexInput.internal);
                         indexOutput.actual, indexInput.expected, indexInput.explicit, indexInput.internal);
 
 
                 output.actual = setter != null ? setter.typeParameters.get(1) : getter.returnType;
                 output.actual = setter != null ? setter.typeParameters.get(1) : getter.returnType;
@@ -130,7 +138,7 @@ public class EBrace extends AExpression {
 
 
             MapSubShortcutNode mapSubShortcutNode = new MapSubShortcutNode();
             MapSubShortcutNode mapSubShortcutNode = new MapSubShortcutNode();
             mapSubShortcutNode.setChildNode(cast(indexOutput.expressionNode, indexCast));
             mapSubShortcutNode.setChildNode(cast(indexOutput.expressionNode, indexCast));
-            mapSubShortcutNode.setLocation(location);
+            mapSubShortcutNode.setLocation(getLocation());
             mapSubShortcutNode.setExpressionType(output.actual);
             mapSubShortcutNode.setExpressionType(output.actual);
             mapSubShortcutNode.setGetter(getter);
             mapSubShortcutNode.setGetter(getter);
             mapSubShortcutNode.setSetter(setter);
             mapSubShortcutNode.setSetter(setter);
@@ -162,8 +170,8 @@ public class EBrace extends AExpression {
             if ((input.read == false || getter != null) && (input.write == false || setter != null)) {
             if ((input.read == false || getter != null) && (input.write == false || setter != null)) {
                 Input indexInput = new Input();
                 Input indexInput = new Input();
                 indexInput.expected = int.class;
                 indexInput.expected = int.class;
-                indexOutput = analyze(index, classNode, scriptRoot, scope, indexInput);
-                indexCast = AnalyzerCaster.getLegalCast(index.location,
+                indexOutput = analyze(indexNode, classNode, scriptRoot, scope, indexInput);
+                indexCast = AnalyzerCaster.getLegalCast(indexNode.getLocation(),
                         indexOutput.actual, indexInput.expected, indexInput.explicit, indexInput.internal);
                         indexOutput.actual, indexInput.expected, indexInput.explicit, indexInput.internal);
 
 
                 output.actual = setter != null ? setter.typeParameters.get(1) : getter.returnType;
                 output.actual = setter != null ? setter.typeParameters.get(1) : getter.returnType;
@@ -173,7 +181,7 @@ public class EBrace extends AExpression {
 
 
             ListSubShortcutNode listSubShortcutNode = new ListSubShortcutNode();
             ListSubShortcutNode listSubShortcutNode = new ListSubShortcutNode();
             listSubShortcutNode.setChildNode(cast(indexOutput.expressionNode, indexCast));
             listSubShortcutNode.setChildNode(cast(indexOutput.expressionNode, indexCast));
-            listSubShortcutNode.setLocation(location);
+            listSubShortcutNode.setLocation(getLocation());
             listSubShortcutNode.setExpressionType(output.actual);
             listSubShortcutNode.setExpressionType(output.actual);
             listSubShortcutNode.setGetter(getter);
             listSubShortcutNode.setGetter(getter);
             listSubShortcutNode.setSetter(setter);
             listSubShortcutNode.setSetter(setter);
@@ -186,7 +194,7 @@ public class EBrace extends AExpression {
         BraceNode braceNode = new BraceNode();
         BraceNode braceNode = new BraceNode();
         braceNode.setLeftNode(prefixOutput.expressionNode);
         braceNode.setLeftNode(prefixOutput.expressionNode);
         braceNode.setRightNode(expressionNode);
         braceNode.setRightNode(expressionNode);
-        braceNode.setLocation(location);
+        braceNode.setLocation(getLocation());
         braceNode.setExpressionType(output.actual);
         braceNode.setExpressionType(output.actual);
 
 
         output.expressionNode = braceNode;
         output.expressionNode = braceNode;

+ 50 - 32
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECall.java

@@ -48,31 +48,49 @@ import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCano
  */
  */
 public class ECall extends AExpression {
 public class ECall extends AExpression {
 
 
-    protected final AExpression prefix;
-    protected final String name;
-    protected final List<AExpression> arguments;
-    protected final boolean nullSafe;
-
-    public ECall(Location location, AExpression prefix, String name, List<AExpression> arguments, boolean nullSafe) {
-        super(location);
-
-        this.prefix = Objects.requireNonNull(prefix);
-        this.name = Objects.requireNonNull(name);
-        this.arguments = Collections.unmodifiableList(Objects.requireNonNull(arguments));
-        this.nullSafe = nullSafe;
+    private final AExpression prefixNode;
+    private final String methodName;
+    private final List<AExpression> argumentNodes;
+    private final boolean isNullSafe;
+
+    public ECall(int identifier, Location location,
+            AExpression prefixNode, String methodName, List<AExpression> argumentNodes, boolean isNullSafe) {
+
+        super(identifier, location);
+
+        this.prefixNode = Objects.requireNonNull(prefixNode);
+        this.methodName = Objects.requireNonNull(methodName);
+        this.argumentNodes = Collections.unmodifiableList(Objects.requireNonNull(argumentNodes));
+        this.isNullSafe = isNullSafe;
+    }
+
+    public AExpression getPrefixNode() {
+        return prefixNode;
+    }
+
+    public String getMethodName() {
+        return methodName;
+    }
+
+    public List<AExpression> getArgumentNodes() {
+        return argumentNodes;
+    }
+
+    public boolean isNullSafe() {
+        return isNullSafe;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         if (input.write) {
         if (input.write) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "invalid assignment: cannot assign a value to method call [" + name + "/" + arguments.size() + "]"));
+                    "invalid assignment: cannot assign a value to method call [" + methodName + "/" + argumentNodes.size() + "]"));
         }
         }
 
 
         Output output = new Output();
         Output output = new Output();
 
 
         Input prefixInput = new Input();
         Input prefixInput = new Input();
-        Output prefixOutput = prefix.analyze(classNode, scriptRoot, scope, prefixInput);
+        Output prefixOutput = prefixNode.analyze(classNode, scriptRoot, scope, prefixInput);
 
 
         if (prefixOutput.partialCanonicalTypeName != null) {
         if (prefixOutput.partialCanonicalTypeName != null) {
             throw createError(new IllegalArgumentException("cannot resolve symbol [" + prefixOutput.partialCanonicalTypeName + "]"));
             throw createError(new IllegalArgumentException("cannot resolve symbol [" + prefixOutput.partialCanonicalTypeName + "]"));
@@ -86,9 +104,9 @@ public class ECall extends AExpression {
                         "instead found unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "]"));
                         "instead found unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "]"));
             }
             }
 
 
-            List<Output> argumentOutputs = new ArrayList<>(arguments.size());
+            List<Output> argumentOutputs = new ArrayList<>(argumentNodes.size());
 
 
-            for (AExpression argument : arguments) {
+            for (AExpression argument : argumentNodes) {
                 Input expressionInput = new Input();
                 Input expressionInput = new Input();
                 expressionInput.internal = true;
                 expressionInput.internal = true;
                 Output expressionOutput = analyze(argument, classNode, scriptRoot, scope, expressionInput);
                 Output expressionOutput = analyze(argument, classNode, scriptRoot, scope, expressionInput);
@@ -96,7 +114,7 @@ public class ECall extends AExpression {
 
 
                 if (expressionOutput.actual == void.class) {
                 if (expressionOutput.actual == void.class) {
                     throw createError(new IllegalArgumentException(
                     throw createError(new IllegalArgumentException(
-                            "Argument(s) cannot be of [void] type when calling method [" + name + "]."));
+                            "Argument(s) cannot be of [void] type when calling method [" + methodName + "]."));
                 }
                 }
             }
             }
 
 
@@ -109,34 +127,34 @@ public class ECall extends AExpression {
                 callSubDefNode.addArgumentNode(argumentOutput.expressionNode);
                 callSubDefNode.addArgumentNode(argumentOutput.expressionNode);
             }
             }
 
 
-            callSubDefNode.setLocation(location);
+            callSubDefNode.setLocation(getLocation());
             callSubDefNode.setExpressionType(output.actual);
             callSubDefNode.setExpressionType(output.actual);
-            callSubDefNode.setName(name);
+            callSubDefNode.setName(methodName);
 
 
             expressionNode = callSubDefNode;
             expressionNode = callSubDefNode;
         } else {
         } else {
             PainlessMethod method = scriptRoot.getPainlessLookup().lookupPainlessMethod(
             PainlessMethod method = scriptRoot.getPainlessLookup().lookupPainlessMethod(
-                    prefixOutput.actual, prefixOutput.isStaticType, name, arguments.size());
+                    prefixOutput.actual, prefixOutput.isStaticType, methodName, argumentNodes.size());
 
 
             if (method == null) {
             if (method == null) {
-                throw createError(new IllegalArgumentException(
-                        "method [" + typeToCanonicalTypeName(prefixOutput.actual) + ", " + name + "/" + arguments.size() + "] not found"));
+                throw createError(new IllegalArgumentException("method [" + typeToCanonicalTypeName(prefixOutput.actual) + ", " +
+                        "" + methodName + "/" + argumentNodes.size() + "] not found"));
             }
             }
 
 
             scriptRoot.markNonDeterministic(method.annotations.containsKey(NonDeterministicAnnotation.class));
             scriptRoot.markNonDeterministic(method.annotations.containsKey(NonDeterministicAnnotation.class));
 
 
-            List<Output> argumentOutputs = new ArrayList<>(arguments.size());
-            List<PainlessCast> argumentCasts = new ArrayList<>(arguments.size());
+            List<Output> argumentOutputs = new ArrayList<>(argumentNodes.size());
+            List<PainlessCast> argumentCasts = new ArrayList<>(argumentOutputs.size());
 
 
-            for (int argument = 0; argument < arguments.size(); ++argument) {
-                AExpression expression = arguments.get(argument);
+            for (int argument = 0; argument < argumentNodes.size(); ++argument) {
+                AExpression expression = argumentNodes.get(argument);
 
 
                 Input expressionInput = new Input();
                 Input expressionInput = new Input();
                 expressionInput.expected = method.typeParameters.get(argument);
                 expressionInput.expected = method.typeParameters.get(argument);
                 expressionInput.internal = true;
                 expressionInput.internal = true;
                 Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
                 Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
                 argumentOutputs.add(expressionOutput);
                 argumentOutputs.add(expressionOutput);
-                argumentCasts.add(AnalyzerCaster.getLegalCast(expression.location,
+                argumentCasts.add(AnalyzerCaster.getLegalCast(expression.getLocation(),
                         expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
                         expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
 
 
             }
             }
@@ -145,25 +163,25 @@ public class ECall extends AExpression {
 
 
             CallSubNode callSubNode = new CallSubNode();
             CallSubNode callSubNode = new CallSubNode();
 
 
-            for (int argument = 0; argument < arguments.size(); ++argument) {
+            for (int argument = 0; argument < argumentNodes.size(); ++argument) {
                 callSubNode.addArgumentNode(cast(argumentOutputs.get(argument).expressionNode, argumentCasts.get(argument)));
                 callSubNode.addArgumentNode(cast(argumentOutputs.get(argument).expressionNode, argumentCasts.get(argument)));
             }
             }
 
 
-            callSubNode.setLocation(location);
+            callSubNode.setLocation(getLocation());
             callSubNode.setExpressionType(output.actual);
             callSubNode.setExpressionType(output.actual);
             callSubNode.setMethod(method);
             callSubNode.setMethod(method);
             callSubNode.setBox(prefixOutput.actual);
             callSubNode.setBox(prefixOutput.actual);
             expressionNode = callSubNode;
             expressionNode = callSubNode;
         }
         }
 
 
-        if (nullSafe) {
+        if (isNullSafe) {
             if (output.actual.isPrimitive()) {
             if (output.actual.isPrimitive()) {
                 throw new IllegalArgumentException("Result of null safe operator must be nullable");
                 throw new IllegalArgumentException("Result of null safe operator must be nullable");
             }
             }
 
 
             NullSafeSubNode nullSafeSubNode = new NullSafeSubNode();
             NullSafeSubNode nullSafeSubNode = new NullSafeSubNode();
             nullSafeSubNode.setChildNode(expressionNode);
             nullSafeSubNode.setChildNode(expressionNode);
-            nullSafeSubNode.setLocation(location);
+            nullSafeSubNode.setLocation(getLocation());
             nullSafeSubNode.setExpressionType(output.actual);
             nullSafeSubNode.setExpressionType(output.actual);
             expressionNode = nullSafeSubNode;
             expressionNode = nullSafeSubNode;
         }
         }
@@ -173,7 +191,7 @@ public class ECall extends AExpression {
         callNode.setLeftNode(prefixOutput.expressionNode);
         callNode.setLeftNode(prefixOutput.expressionNode);
         callNode.setRightNode(expressionNode);
         callNode.setRightNode(expressionNode);
 
 
-        callNode.setLocation(location);
+        callNode.setLocation(getLocation());
         callNode.setExpressionType(output.actual);
         callNode.setExpressionType(output.actual);
 
 
         output.expressionNode = callNode;
         output.expressionNode = callNode;

+ 30 - 22
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java

@@ -44,21 +44,29 @@ import java.util.Objects;
  */
  */
 public class ECallLocal extends AExpression {
 public class ECallLocal extends AExpression {
 
 
-    protected final String name;
-    protected final List<AExpression> arguments;
+    private final String methodName;
+    private final List<AExpression> argumentNodes;
 
 
-    public ECallLocal(Location location, String name, List<AExpression> arguments) {
-        super(location);
+    public ECallLocal(int identifier, Location location, String methodName, List<AExpression> argumentNodes) {
+        super(identifier, location);
 
 
-        this.name = Objects.requireNonNull(name);
-        this.arguments = Collections.unmodifiableList(Objects.requireNonNull(arguments));
+        this.methodName = Objects.requireNonNull(methodName);
+        this.argumentNodes = Collections.unmodifiableList(Objects.requireNonNull(argumentNodes));
+    }
+
+    public String getMethodName() {
+        return methodName;
+    }
+
+    public List<AExpression> getArgumentNodes() {
+        return argumentNodes;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         if (input.write) {
         if (input.write) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "invalid assignment: cannot assign a value to function call [" + name + "/" + arguments.size() + "]"));
+                    "invalid assignment: cannot assign a value to function call [" + methodName + "/" + argumentNodes.size() + "]"));
         }
         }
 
 
         FunctionTable.LocalFunction localFunction = null;
         FunctionTable.LocalFunction localFunction = null;
@@ -70,7 +78,7 @@ public class ECallLocal extends AExpression {
 
 
         Output output = new Output();
         Output output = new Output();
 
 
-        localFunction = scriptRoot.getFunctionTable().getFunction(name, arguments.size());
+        localFunction = scriptRoot.getFunctionTable().getFunction(methodName, argumentNodes.size());
 
 
         // user cannot call internal functions, reset to null if an internal function is found
         // user cannot call internal functions, reset to null if an internal function is found
         if (localFunction != null && localFunction.isInternal()) {
         if (localFunction != null && localFunction.isInternal()) {
@@ -78,10 +86,10 @@ public class ECallLocal extends AExpression {
         }
         }
 
 
         if (localFunction == null) {
         if (localFunction == null) {
-            importedMethod = scriptRoot.getPainlessLookup().lookupImportedPainlessMethod(name, arguments.size());
+            importedMethod = scriptRoot.getPainlessLookup().lookupImportedPainlessMethod(methodName, argumentNodes.size());
 
 
             if (importedMethod == null) {
             if (importedMethod == null) {
-                classBinding = scriptRoot.getPainlessLookup().lookupPainlessClassBinding(name, arguments.size());
+                classBinding = scriptRoot.getPainlessLookup().lookupPainlessClassBinding(methodName, argumentNodes.size());
 
 
                 // check to see if this class binding requires an implicit this reference
                 // check to see if this class binding requires an implicit this reference
                 if (classBinding != null && classBinding.typeParameters.isEmpty() == false &&
                 if (classBinding != null && classBinding.typeParameters.isEmpty() == false &&
@@ -96,7 +104,7 @@ public class ECallLocal extends AExpression {
                     // will likely involve adding a class instance binding where any instance can have a class binding
                     // will likely involve adding a class instance binding where any instance can have a class binding
                     // as part of its API.  However, the situation at run-time is difficult and will modifications that
                     // as part of its API.  However, the situation at run-time is difficult and will modifications that
                     // are a substantial change if even possible to do.
                     // are a substantial change if even possible to do.
-                    classBinding = scriptRoot.getPainlessLookup().lookupPainlessClassBinding(name, arguments.size() + 1);
+                    classBinding = scriptRoot.getPainlessLookup().lookupPainlessClassBinding(methodName, argumentNodes.size() + 1);
 
 
                     if (classBinding != null) {
                     if (classBinding != null) {
                         if (classBinding.typeParameters.isEmpty() == false &&
                         if (classBinding.typeParameters.isEmpty() == false &&
@@ -108,11 +116,11 @@ public class ECallLocal extends AExpression {
                     }
                     }
 
 
                     if (classBinding == null) {
                     if (classBinding == null) {
-                        instanceBinding = scriptRoot.getPainlessLookup().lookupPainlessInstanceBinding(name, arguments.size());
+                        instanceBinding = scriptRoot.getPainlessLookup().lookupPainlessInstanceBinding(methodName, argumentNodes.size());
 
 
                         if (instanceBinding == null) {
                         if (instanceBinding == null) {
                             throw createError(new IllegalArgumentException(
                             throw createError(new IllegalArgumentException(
-                                    "Unknown call [" + name + "] with [" + arguments.size() + "] arguments."));
+                                    "Unknown call [" + methodName + "] with [" + argumentNodes.size() + "] arguments."));
                         }
                         }
                     }
                     }
                 }
                 }
@@ -135,7 +143,7 @@ public class ECallLocal extends AExpression {
             bindingName = scriptRoot.getNextSyntheticName("class_binding");
             bindingName = scriptRoot.getNextSyntheticName("class_binding");
 
 
             FieldNode fieldNode = new FieldNode();
             FieldNode fieldNode = new FieldNode();
-            fieldNode.setLocation(location);
+            fieldNode.setLocation(getLocation());
             fieldNode.setModifiers(Modifier.PRIVATE);
             fieldNode.setModifiers(Modifier.PRIVATE);
             fieldNode.setFieldType(classBinding.javaConstructor.getDeclaringClass());
             fieldNode.setFieldType(classBinding.javaConstructor.getDeclaringClass());
             fieldNode.setName(bindingName);
             fieldNode.setName(bindingName);
@@ -147,7 +155,7 @@ public class ECallLocal extends AExpression {
             bindingName = scriptRoot.getNextSyntheticName("instance_binding");
             bindingName = scriptRoot.getNextSyntheticName("instance_binding");
 
 
             FieldNode fieldNode = new FieldNode();
             FieldNode fieldNode = new FieldNode();
-            fieldNode.setLocation(location);
+            fieldNode.setLocation(getLocation());
             fieldNode.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
             fieldNode.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
             fieldNode.setFieldType(instanceBinding.targetInstance.getClass());
             fieldNode.setFieldType(instanceBinding.targetInstance.getClass());
             fieldNode.setName(bindingName);
             fieldNode.setName(bindingName);
@@ -159,31 +167,31 @@ public class ECallLocal extends AExpression {
             throw new IllegalStateException("Illegal tree structure.");
             throw new IllegalStateException("Illegal tree structure.");
         }
         }
 
 
-        List<Output> argumentOutputs = new ArrayList<>(arguments.size());
-        List<PainlessCast> argumentCasts = new ArrayList<>(arguments.size());
+        List<Output> argumentOutputs = new ArrayList<>(argumentNodes.size());
+        List<PainlessCast> argumentCasts = new ArrayList<>(argumentNodes.size());
         // if the class binding is using an implicit this reference then the arguments counted must
         // if the class binding is using an implicit this reference then the arguments counted must
         // be incremented by 1 as the this reference will not be part of the arguments passed into
         // be incremented by 1 as the this reference will not be part of the arguments passed into
         // the class binding call
         // the class binding call
-        for (int argument = 0; argument < arguments.size(); ++argument) {
-            AExpression expression = arguments.get(argument);
+        for (int argument = 0; argument < argumentNodes.size(); ++argument) {
+            AExpression expression = argumentNodes.get(argument);
 
 
             Input argumentInput = new Input();
             Input argumentInput = new Input();
             argumentInput.expected = typeParameters.get(argument + classBindingOffset);
             argumentInput.expected = typeParameters.get(argument + classBindingOffset);
             argumentInput.internal = true;
             argumentInput.internal = true;
             Output argumentOutput = analyze(expression, classNode, scriptRoot, scope, argumentInput);
             Output argumentOutput = analyze(expression, classNode, scriptRoot, scope, argumentInput);
             argumentOutputs.add(argumentOutput);
             argumentOutputs.add(argumentOutput);
-            argumentCasts.add(AnalyzerCaster.getLegalCast(expression.location,
+            argumentCasts.add(AnalyzerCaster.getLegalCast(expression.getLocation(),
                     argumentOutput.actual, argumentInput.expected, argumentInput.explicit, argumentInput.internal));
                     argumentOutput.actual, argumentInput.expected, argumentInput.explicit, argumentInput.internal));
 
 
         }
         }
 
 
         MemberCallNode memberCallNode = new MemberCallNode();
         MemberCallNode memberCallNode = new MemberCallNode();
 
 
-        for (int argument = 0; argument < arguments.size(); ++argument) {
+        for (int argument = 0; argument < argumentNodes.size(); ++argument) {
             memberCallNode.addArgumentNode(cast(argumentOutputs.get(argument).expressionNode, argumentCasts.get(argument)));
             memberCallNode.addArgumentNode(cast(argumentOutputs.get(argument).expressionNode, argumentCasts.get(argument)));
         }
         }
 
 
-        memberCallNode.setLocation(location);
+        memberCallNode.setLocation(getLocation());
         memberCallNode.setExpressionType(output.actual);
         memberCallNode.setExpressionType(output.actual);
         memberCallNode.setLocalFunction(localFunction);
         memberCallNode.setLocalFunction(localFunction);
         memberCallNode.setImportedMethod(importedMethod);
         memberCallNode.setImportedMethod(importedMethod);

+ 25 - 13
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EComp.java

@@ -37,16 +37,28 @@ import java.util.Objects;
  */
  */
 public class EComp extends AExpression {
 public class EComp extends AExpression {
 
 
-    protected final Operation operation;
-    protected final AExpression left;
-    protected final AExpression right;
+    private final AExpression leftNode;
+    private final AExpression rightNode;
+    private final Operation operation;
 
 
-    public EComp(Location location, Operation operation, AExpression left, AExpression right) {
-        super(location);
+    public EComp(int identifier, Location location, AExpression leftNode, AExpression rightNode, Operation operation) {
+        super(identifier, location);
 
 
         this.operation = Objects.requireNonNull(operation);
         this.operation = Objects.requireNonNull(operation);
-        this.left = Objects.requireNonNull(left);
-        this.right = Objects.requireNonNull(right);
+        this.leftNode = Objects.requireNonNull(leftNode);
+        this.rightNode = Objects.requireNonNull(rightNode);
+    }
+
+    public AExpression getLeftNode() {
+        return leftNode;
+    }
+
+    public AExpression getRightNode() {
+        return rightNode;
+    }
+
+    public Operation getOperation() {
+        return operation;
     }
     }
 
 
     @Override
     @Override
@@ -66,10 +78,10 @@ public class EComp extends AExpression {
         Output output = new Output();
         Output output = new Output();
 
 
         Input leftInput = new Input();
         Input leftInput = new Input();
-        Output leftOutput = analyze(left, classNode, scriptRoot, scope, leftInput);
+        Output leftOutput = analyze(leftNode, classNode, scriptRoot, scope, leftInput);
 
 
         Input rightInput = new Input();
         Input rightInput = new Input();
-        Output rightOutput = analyze(right, classNode, scriptRoot, scope, rightInput);
+        Output rightOutput = analyze(rightNode, classNode, scriptRoot, scope, rightInput);
 
 
         if (operation == Operation.EQ || operation == Operation.EQR || operation == Operation.NE || operation == Operation.NER) {
         if (operation == Operation.EQ || operation == Operation.EQR || operation == Operation.NE || operation == Operation.NER) {
             promotedType = AnalyzerCaster.promoteEquality(leftOutput.actual, rightOutput.actual);
             promotedType = AnalyzerCaster.promoteEquality(leftOutput.actual, rightOutput.actual);
@@ -95,13 +107,13 @@ public class EComp extends AExpression {
         }
         }
 
 
         if ((operation == Operation.EQ || operation == Operation.EQR || operation == Operation.NE || operation == Operation.NER)
         if ((operation == Operation.EQ || operation == Operation.EQR || operation == Operation.NE || operation == Operation.NER)
-                && left.getChildIf(ENull.class) != null && right.getChildIf(ENull.class) != null) {
+                && leftNode instanceof ENull && rightNode instanceof ENull) {
             throw createError(new IllegalArgumentException("extraneous comparison of [null] constants"));
             throw createError(new IllegalArgumentException("extraneous comparison of [null] constants"));
         }
         }
 
 
-        PainlessCast leftCast = AnalyzerCaster.getLegalCast(left.location,
+        PainlessCast leftCast = AnalyzerCaster.getLegalCast(leftNode.getLocation(),
                 leftOutput.actual, leftInput.expected, leftInput.explicit, leftInput.internal);
                 leftOutput.actual, leftInput.expected, leftInput.explicit, leftInput.internal);
-        PainlessCast rightCast = AnalyzerCaster.getLegalCast(right.location,
+        PainlessCast rightCast = AnalyzerCaster.getLegalCast(rightNode.getLocation(),
                 rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
                 rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
 
 
         output.actual = boolean.class;
         output.actual = boolean.class;
@@ -111,7 +123,7 @@ public class EComp extends AExpression {
         comparisonNode.setLeftNode(cast(leftOutput.expressionNode, leftCast));
         comparisonNode.setLeftNode(cast(leftOutput.expressionNode, leftCast));
         comparisonNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
         comparisonNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
 
 
-        comparisonNode.setLocation(location);
+        comparisonNode.setLocation(getLocation());
         comparisonNode.setExpressionType(output.actual);
         comparisonNode.setExpressionType(output.actual);
         comparisonNode.setComparisonType(promotedType);
         comparisonNode.setComparisonType(promotedType);
         comparisonNode.setOperation(operation);
         comparisonNode.setOperation(operation);

+ 27 - 15
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConditional.java

@@ -35,16 +35,28 @@ import java.util.Objects;
  */
  */
 public class EConditional extends AExpression {
 public class EConditional extends AExpression {
 
 
-    protected final AExpression condition;
-    protected final AExpression left;
-    protected final AExpression right;
+    private final AExpression conditionNode;
+    private final AExpression leftNode;
+    private final AExpression rightNode;
 
 
-    public EConditional(Location location, AExpression condition, AExpression left, AExpression right) {
-        super(location);
+    public EConditional(int identifier, Location location, AExpression conditionNode, AExpression leftNode, AExpression rightNode) {
+        super(identifier, location);
 
 
-        this.condition = Objects.requireNonNull(condition);
-        this.left = Objects.requireNonNull(left);
-        this.right = Objects.requireNonNull(right);
+        this.conditionNode = Objects.requireNonNull(conditionNode);
+        this.leftNode = Objects.requireNonNull(leftNode);
+        this.rightNode = Objects.requireNonNull(rightNode);
+    }
+
+    public AExpression getConditionNode() {
+        return conditionNode;
+    }
+
+    public AExpression getLeftNode() {
+        return leftNode;
+    }
+
+    public AExpression getRightNode() {
+        return rightNode;
     }
     }
 
 
     @Override
     @Override
@@ -61,21 +73,21 @@ public class EConditional extends AExpression {
 
 
         Input conditionInput = new Input();
         Input conditionInput = new Input();
         conditionInput.expected = boolean.class;
         conditionInput.expected = boolean.class;
-        Output conditionOutput = analyze(condition, classNode, scriptRoot, scope, conditionInput);
-        PainlessCast conditionCast = AnalyzerCaster.getLegalCast(condition.location,
+        Output conditionOutput = analyze(conditionNode, classNode, scriptRoot, scope, conditionInput);
+        PainlessCast conditionCast = AnalyzerCaster.getLegalCast(classNode.getLocation(),
                 conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
                 conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
 
 
         Input leftInput = new Input();
         Input leftInput = new Input();
         leftInput.expected = input.expected;
         leftInput.expected = input.expected;
         leftInput.explicit = input.explicit;
         leftInput.explicit = input.explicit;
         leftInput.internal = input.internal;
         leftInput.internal = input.internal;
-        Output leftOutput = analyze(left, classNode, scriptRoot, scope, leftInput);
+        Output leftOutput = analyze(leftNode, classNode, scriptRoot, scope, leftInput);
 
 
         Input rightInput = new Input();
         Input rightInput = new Input();
         rightInput.expected = input.expected;
         rightInput.expected = input.expected;
         rightInput.explicit = input.explicit;
         rightInput.explicit = input.explicit;
         rightInput.internal = input.internal;
         rightInput.internal = input.internal;
-        Output rightOutput = analyze(right, classNode, scriptRoot, scope, rightInput);
+        Output rightOutput = analyze(rightNode, classNode, scriptRoot, scope, rightInput);
 
 
         output.actual = input.expected;
         output.actual = input.expected;
 
 
@@ -93,9 +105,9 @@ public class EConditional extends AExpression {
             output.actual = promote;
             output.actual = promote;
         }
         }
 
 
-        PainlessCast leftCast = AnalyzerCaster.getLegalCast(left.location,
+        PainlessCast leftCast = AnalyzerCaster.getLegalCast(leftNode.getLocation(),
                 leftOutput.actual, leftInput.expected, leftInput.explicit, leftInput.internal);
                 leftOutput.actual, leftInput.expected, leftInput.explicit, leftInput.internal);
-        PainlessCast rightCast = AnalyzerCaster.getLegalCast(right.location,
+        PainlessCast rightCast = AnalyzerCaster.getLegalCast(rightNode.getLocation(),
                 rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
                 rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
 
 
         ConditionalNode conditionalNode = new ConditionalNode();
         ConditionalNode conditionalNode = new ConditionalNode();
@@ -104,7 +116,7 @@ public class EConditional extends AExpression {
         conditionalNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
         conditionalNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
         conditionalNode.setConditionNode(cast(conditionOutput.expressionNode, conditionCast));
         conditionalNode.setConditionNode(cast(conditionOutput.expressionNode, conditionCast));
 
 
-        conditionalNode.setLocation(location);
+        conditionalNode.setLocation(getLocation());
         conditionalNode.setExpressionType(output.actual);
         conditionalNode.setExpressionType(output.actual);
 
 
         output.expressionNode = conditionalNode;
         output.expressionNode = conditionalNode;

+ 11 - 5
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConstant.java

@@ -26,18 +26,24 @@ import org.elasticsearch.painless.ir.ConstantNode;
 import org.elasticsearch.painless.lookup.PainlessLookupUtility;
 import org.elasticsearch.painless.lookup.PainlessLookupUtility;
 import org.elasticsearch.painless.symbol.ScriptRoot;
 import org.elasticsearch.painless.symbol.ScriptRoot;
 
 
+import java.util.Objects;
+
 /**
 /**
  * Represents a constant inserted into the tree replacing
  * Represents a constant inserted into the tree replacing
  * other constants during constant folding.  (Internal only.)
  * other constants during constant folding.  (Internal only.)
  */
  */
 public class EConstant extends AExpression {
 public class EConstant extends AExpression {
 
 
-    protected Object constant;
+    private final Object constant;
+
+    public EConstant(int identifier, Location location, Object constant) {
+        super(identifier, location);
 
 
-    public EConstant(Location location, Object constant) {
-        super(location);
+        this.constant = Objects.requireNonNull(constant);
+    }
 
 
-        this.constant = constant;
+    public Object getConstant() {
+        return constant;
     }
     }
 
 
     @Override
     @Override
@@ -77,7 +83,7 @@ public class EConstant extends AExpression {
         }
         }
 
 
         ConstantNode constantNode = new ConstantNode();
         ConstantNode constantNode = new ConstantNode();
-        constantNode.setLocation(location);
+        constantNode.setLocation(getLocation());
         constantNode.setExpressionType(output.actual);
         constantNode.setExpressionType(output.actual);
         constantNode.setConstant(constant);
         constantNode.setConstant(constant);
 
 

+ 21 - 17
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EDecimal.java

@@ -32,56 +32,60 @@ import java.util.Objects;
  */
  */
 public class EDecimal extends AExpression {
 public class EDecimal extends AExpression {
 
 
-    protected final String value;
+    private final String decimal;
 
 
-    public EDecimal(Location location, String value) {
-        super(location);
+    public EDecimal(int identifier, Location location, String decimal) {
+        super(identifier, location);
 
 
-        this.value = Objects.requireNonNull(value);
+        this.decimal = Objects.requireNonNull(decimal);
+    }
+
+    public String getDecimal() {
+        return decimal;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
-        return analyze(classNode, scriptRoot, scope, input, false);
+        return analyze(input, false);
     }
     }
 
 
-    Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input, boolean negate) {
+    Output analyze(Input input, boolean negate) {
         if (input.write) {
         if (input.write) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "invalid assignment: cannot assign a value to decimal constant [" + value + "]"));
+                    "invalid assignment: cannot assign a value to decimal constant [" + decimal + "]"));
         }
         }
 
 
         if (input.read == false) {
         if (input.read == false) {
-            throw createError(new IllegalArgumentException("not a statement: decimal constant [" + value + "] not used"));
+            throw createError(new IllegalArgumentException("not a statement: decimal constant [" + decimal + "] not used"));
         }
         }
 
 
         Output output = new Output();
         Output output = new Output();
         Object constant;
         Object constant;
 
 
-        String value = negate ? "-" + this.value : this.value;
+        String decimal = negate ? "-" + this.decimal : this.decimal;
 
 
-        if (value.endsWith("f") || value.endsWith("F")) {
+        if (decimal.endsWith("f") || decimal.endsWith("F")) {
             try {
             try {
-                constant = Float.parseFloat(value.substring(0, value.length() - 1));
+                constant = Float.parseFloat(decimal.substring(0, decimal.length() - 1));
                 output.actual = float.class;
                 output.actual = float.class;
             } catch (NumberFormatException exception) {
             } catch (NumberFormatException exception) {
-                throw createError(new IllegalArgumentException("Invalid float constant [" + value + "]."));
+                throw createError(new IllegalArgumentException("Invalid float constant [" + decimal + "]."));
             }
             }
         } else {
         } else {
-            String toParse = value;
-            if (toParse.endsWith("d") || value.endsWith("D")) {
-                toParse = toParse.substring(0, value.length() - 1);
+            String toParse = decimal;
+            if (toParse.endsWith("d") || decimal.endsWith("D")) {
+                toParse = toParse.substring(0, decimal.length() - 1);
             }
             }
             try {
             try {
                 constant = Double.parseDouble(toParse);
                 constant = Double.parseDouble(toParse);
                 output.actual = double.class;
                 output.actual = double.class;
             } catch (NumberFormatException exception) {
             } catch (NumberFormatException exception) {
-                throw createError(new IllegalArgumentException("Invalid double constant [" + value + "]."));
+                throw createError(new IllegalArgumentException("Invalid double constant [" + decimal + "]."));
             }
             }
         }
         }
 
 
         ConstantNode constantNode = new ConstantNode();
         ConstantNode constantNode = new ConstantNode();
-        constantNode.setLocation(location);
+        constantNode.setLocation(getLocation());
         constantNode.setExpressionType(output.actual);
         constantNode.setExpressionType(output.actual);
         constantNode.setConstant(constant);
         constantNode.setConstant(constant);
 
 

+ 48 - 36
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EDot.java

@@ -52,16 +52,28 @@ import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCano
  */
  */
 public class EDot extends AExpression {
 public class EDot extends AExpression {
 
 
-    protected final AExpression prefix;
-    protected final boolean nullSafe;
-    protected final String value;
+    private final AExpression prefixNode;
+    private final String index;
+    private final boolean isNullSafe;
 
 
-    public EDot(Location location, AExpression prefix, boolean nullSafe, String value) {
-        super(location);
+    public EDot(int identifier, Location location, AExpression prefixNode, String index, boolean isNullSafe) {
+        super(identifier, location);
 
 
-        this.prefix = Objects.requireNonNull(prefix);
-        this.nullSafe = nullSafe;
-        this.value = Objects.requireNonNull(value);
+        this.prefixNode = Objects.requireNonNull(prefixNode);
+        this.index = Objects.requireNonNull(index);
+        this.isNullSafe = isNullSafe;
+    }
+
+    public AExpression getPrefixNode() {
+        return prefixNode;
+    }
+
+    public String getIndex() {
+        return index;
+    }
+
+    public boolean isNullSafe() {
+        return isNullSafe;
     }
     }
 
 
     @Override
     @Override
@@ -71,7 +83,7 @@ public class EDot extends AExpression {
         }
         }
 
 
         Output output = new Output();
         Output output = new Output();
-        Output prefixOutput = prefix.analyze(classNode, scriptRoot, scope, new Input());
+        Output prefixOutput = prefixNode.analyze(classNode, scriptRoot, scope, new Input());
 
 
         if (prefixOutput.partialCanonicalTypeName != null) {
         if (prefixOutput.partialCanonicalTypeName != null) {
             if (output.isStaticType) {
             if (output.isStaticType) {
@@ -79,7 +91,7 @@ public class EDot extends AExpression {
                         "instead found unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "]"));
                         "instead found unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "]"));
             }
             }
 
 
-            String canonicalTypeName = prefixOutput.partialCanonicalTypeName + "." + value;
+            String canonicalTypeName = prefixOutput.partialCanonicalTypeName + "." + index;
             Class<?> type = scriptRoot.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
             Class<?> type = scriptRoot.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
 
 
             if (type == null) {
             if (type == null) {
@@ -100,7 +112,7 @@ public class EDot extends AExpression {
 
 
                 StaticNode staticNode = new StaticNode();
                 StaticNode staticNode = new StaticNode();
 
 
-                staticNode.setLocation(location);
+                staticNode.setLocation(getLocation());
                 staticNode.setExpressionType(output.actual);
                 staticNode.setExpressionType(output.actual);
 
 
                 output.expressionNode = staticNode;
                 output.expressionNode = staticNode;
@@ -117,7 +129,7 @@ public class EDot extends AExpression {
                             "instead found unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "]"));
                             "instead found unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "]"));
                 }
                 }
 
 
-                if ("length".equals(value)) {
+                if ("length".equals(index)) {
                     if (input.write) {
                     if (input.write) {
                         throw createError(new IllegalArgumentException(
                         throw createError(new IllegalArgumentException(
                                 "invalid assignment: cannot assign a value write to read-only field [length] for an array."));
                                 "invalid assignment: cannot assign a value write to read-only field [length] for an array."));
@@ -126,11 +138,11 @@ public class EDot extends AExpression {
                     output.actual = int.class;
                     output.actual = int.class;
                 } else {
                 } else {
                     throw createError(new IllegalArgumentException(
                     throw createError(new IllegalArgumentException(
-                            "Field [" + value + "] does not exist for type [" + targetCanonicalTypeName + "]."));
+                            "Field [" + index + "] does not exist for type [" + targetCanonicalTypeName + "]."));
                 }
                 }
 
 
                 DotSubArrayLengthNode dotSubArrayLengthNode = new DotSubArrayLengthNode();
                 DotSubArrayLengthNode dotSubArrayLengthNode = new DotSubArrayLengthNode();
-                dotSubArrayLengthNode.setLocation(location);
+                dotSubArrayLengthNode.setLocation(getLocation());
                 dotSubArrayLengthNode.setExpressionType(output.actual);
                 dotSubArrayLengthNode.setExpressionType(output.actual);
                 expressionNode = dotSubArrayLengthNode;
                 expressionNode = dotSubArrayLengthNode;
             } else if (prefixOutput.actual == def.class) {
             } else if (prefixOutput.actual == def.class) {
@@ -145,38 +157,38 @@ public class EDot extends AExpression {
                 output.isDefOptimized = true;
                 output.isDefOptimized = true;
 
 
                 DotSubDefNode dotSubDefNode = new DotSubDefNode();
                 DotSubDefNode dotSubDefNode = new DotSubDefNode();
-                dotSubDefNode.setLocation(location);
+                dotSubDefNode.setLocation(getLocation());
                 dotSubDefNode.setExpressionType(output.actual);
                 dotSubDefNode.setExpressionType(output.actual);
-                dotSubDefNode.setValue(value);
+                dotSubDefNode.setValue(index);
                 expressionNode = dotSubDefNode;
                 expressionNode = dotSubDefNode;
             } else {
             } else {
                 PainlessField field =
                 PainlessField field =
-                        scriptRoot.getPainlessLookup().lookupPainlessField(prefixOutput.actual, prefixOutput.isStaticType, value);
+                        scriptRoot.getPainlessLookup().lookupPainlessField(prefixOutput.actual, prefixOutput.isStaticType, index);
 
 
                 if (field == null) {
                 if (field == null) {
                     PainlessMethod getter;
                     PainlessMethod getter;
                     PainlessMethod setter;
                     PainlessMethod setter;
 
 
                     getter = scriptRoot.getPainlessLookup().lookupPainlessMethod(prefixOutput.actual, false,
                     getter = scriptRoot.getPainlessLookup().lookupPainlessMethod(prefixOutput.actual, false,
-                            "get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0);
+                            "get" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
 
 
                     if (getter == null) {
                     if (getter == null) {
                         getter = scriptRoot.getPainlessLookup().lookupPainlessMethod(prefixOutput.actual, false,
                         getter = scriptRoot.getPainlessLookup().lookupPainlessMethod(prefixOutput.actual, false,
-                                "is" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0);
+                                "is" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
                     }
                     }
 
 
                     setter = scriptRoot.getPainlessLookup().lookupPainlessMethod(prefixOutput.actual, false,
                     setter = scriptRoot.getPainlessLookup().lookupPainlessMethod(prefixOutput.actual, false,
-                            "set" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0);
+                            "set" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
 
 
                     if (getter != null || setter != null) {
                     if (getter != null || setter != null) {
                         if (getter != null && (getter.returnType == void.class || !getter.typeParameters.isEmpty())) {
                         if (getter != null && (getter.returnType == void.class || !getter.typeParameters.isEmpty())) {
                             throw createError(new IllegalArgumentException(
                             throw createError(new IllegalArgumentException(
-                                    "Illegal get shortcut on field [" + value + "] for type [" + targetCanonicalTypeName + "]."));
+                                    "Illegal get shortcut on field [" + index + "] for type [" + targetCanonicalTypeName + "]."));
                         }
                         }
 
 
                         if (setter != null && (setter.returnType != void.class || setter.typeParameters.size() != 1)) {
                         if (setter != null && (setter.returnType != void.class || setter.typeParameters.size() != 1)) {
                             throw createError(new IllegalArgumentException(
                             throw createError(new IllegalArgumentException(
-                                    "Illegal set shortcut on field [" + value + "] for type [" + targetCanonicalTypeName + "]."));
+                                    "Illegal set shortcut on field [" + index + "] for type [" + targetCanonicalTypeName + "]."));
                         }
                         }
 
 
                         if (getter != null && setter != null && setter.typeParameters.get(0) != getter.returnType) {
                         if (getter != null && setter != null && setter.typeParameters.get(0) != getter.returnType) {
@@ -187,11 +199,11 @@ public class EDot extends AExpression {
                             output.actual = setter != null ? setter.typeParameters.get(0) : getter.returnType;
                             output.actual = setter != null ? setter.typeParameters.get(0) : getter.returnType;
                         } else {
                         } else {
                             throw createError(new IllegalArgumentException(
                             throw createError(new IllegalArgumentException(
-                                    "Illegal shortcut on field [" + value + "] for type [" + targetCanonicalTypeName + "]."));
+                                    "Illegal shortcut on field [" + index + "] for type [" + targetCanonicalTypeName + "]."));
                         }
                         }
 
 
                         DotSubShortcutNode dotSubShortcutNode = new DotSubShortcutNode();
                         DotSubShortcutNode dotSubShortcutNode = new DotSubShortcutNode();
-                        dotSubShortcutNode.setLocation(location);
+                        dotSubShortcutNode.setLocation(getLocation());
                         dotSubShortcutNode.setExpressionType(output.actual);
                         dotSubShortcutNode.setExpressionType(output.actual);
                         dotSubShortcutNode.setGetter(getter);
                         dotSubShortcutNode.setGetter(getter);
                         dotSubShortcutNode.setSetter(setter);
                         dotSubShortcutNode.setSetter(setter);
@@ -224,13 +236,13 @@ public class EDot extends AExpression {
                             }
                             }
 
 
                             ConstantNode constantNode = new ConstantNode();
                             ConstantNode constantNode = new ConstantNode();
-                            constantNode.setLocation(location);
+                            constantNode.setLocation(getLocation());
                             constantNode.setExpressionType(String.class);
                             constantNode.setExpressionType(String.class);
-                            constantNode.setConstant(value);
+                            constantNode.setConstant(index);
 
 
                             MapSubShortcutNode mapSubShortcutNode = new MapSubShortcutNode();
                             MapSubShortcutNode mapSubShortcutNode = new MapSubShortcutNode();
                             mapSubShortcutNode.setChildNode(constantNode);
                             mapSubShortcutNode.setChildNode(constantNode);
-                            mapSubShortcutNode.setLocation(location);
+                            mapSubShortcutNode.setLocation(getLocation());
                             mapSubShortcutNode.setExpressionType(output.actual);
                             mapSubShortcutNode.setExpressionType(output.actual);
                             mapSubShortcutNode.setGetter(getter);
                             mapSubShortcutNode.setGetter(getter);
                             mapSubShortcutNode.setSetter(setter);
                             mapSubShortcutNode.setSetter(setter);
@@ -241,9 +253,9 @@ public class EDot extends AExpression {
                             int index;
                             int index;
 
 
                             try {
                             try {
-                                index = Integer.parseInt(value);
+                                index = Integer.parseInt(this.index);
                             } catch (NumberFormatException nfe) {
                             } catch (NumberFormatException nfe) {
-                                throw createError(new IllegalArgumentException("invalid list index [" + value + "]"));
+                                throw createError(new IllegalArgumentException("invalid list index [" + this.index + "]"));
                             }
                             }
 
 
                             getter = scriptRoot.getPainlessLookup().lookupPainlessMethod(targetType, false, "get", 1);
                             getter = scriptRoot.getPainlessLookup().lookupPainlessMethod(targetType, false, "get", 1);
@@ -273,13 +285,13 @@ public class EDot extends AExpression {
                             }
                             }
 
 
                             ConstantNode constantNode = new ConstantNode();
                             ConstantNode constantNode = new ConstantNode();
-                            constantNode.setLocation(location);
+                            constantNode.setLocation(getLocation());
                             constantNode.setExpressionType(int.class);
                             constantNode.setExpressionType(int.class);
                             constantNode.setConstant(index);
                             constantNode.setConstant(index);
 
 
                             ListSubShortcutNode listSubShortcutNode = new ListSubShortcutNode();
                             ListSubShortcutNode listSubShortcutNode = new ListSubShortcutNode();
                             listSubShortcutNode.setChildNode(constantNode);
                             listSubShortcutNode.setChildNode(constantNode);
-                            listSubShortcutNode.setLocation(location);
+                            listSubShortcutNode.setLocation(getLocation());
                             listSubShortcutNode.setExpressionType(output.actual);
                             listSubShortcutNode.setExpressionType(output.actual);
                             listSubShortcutNode.setGetter(getter);
                             listSubShortcutNode.setGetter(getter);
                             listSubShortcutNode.setSetter(setter);
                             listSubShortcutNode.setSetter(setter);
@@ -289,7 +301,7 @@ public class EDot extends AExpression {
 
 
                     if (expressionNode == null) {
                     if (expressionNode == null) {
                         throw createError(new IllegalArgumentException(
                         throw createError(new IllegalArgumentException(
-                                "field [" + typeToCanonicalTypeName(prefixOutput.actual) + ", " + value + "] not found"));
+                                "field [" + typeToCanonicalTypeName(prefixOutput.actual) + ", " + index + "] not found"));
                     }
                     }
                 } else {
                 } else {
                     if (input.write && Modifier.isFinal(field.javaField.getModifiers())) {
                     if (input.write && Modifier.isFinal(field.javaField.getModifiers())) {
@@ -300,14 +312,14 @@ public class EDot extends AExpression {
                     output.actual = field.typeParameter;
                     output.actual = field.typeParameter;
 
 
                     DotSubNode dotSubNode = new DotSubNode();
                     DotSubNode dotSubNode = new DotSubNode();
-                    dotSubNode.setLocation(location);
+                    dotSubNode.setLocation(getLocation());
                     dotSubNode.setExpressionType(output.actual);
                     dotSubNode.setExpressionType(output.actual);
                     dotSubNode.setField(field);
                     dotSubNode.setField(field);
                     expressionNode = dotSubNode;
                     expressionNode = dotSubNode;
                 }
                 }
             }
             }
 
 
-            if (nullSafe) {
+            if (isNullSafe) {
                 if (input.write) {
                 if (input.write) {
                     throw createError(new IllegalArgumentException(
                     throw createError(new IllegalArgumentException(
                             "invalid assignment: cannot assign a value to a null safe operation [?.]"));
                             "invalid assignment: cannot assign a value to a null safe operation [?.]"));
@@ -319,7 +331,7 @@ public class EDot extends AExpression {
 
 
                 NullSafeSubNode nullSafeSubNode = new NullSafeSubNode();
                 NullSafeSubNode nullSafeSubNode = new NullSafeSubNode();
                 nullSafeSubNode.setChildNode(expressionNode);
                 nullSafeSubNode.setChildNode(expressionNode);
-                nullSafeSubNode.setLocation(location);
+                nullSafeSubNode.setLocation(getLocation());
                 nullSafeSubNode.setExpressionType(output.actual);
                 nullSafeSubNode.setExpressionType(output.actual);
                 expressionNode = nullSafeSubNode;
                 expressionNode = nullSafeSubNode;
             }
             }
@@ -327,7 +339,7 @@ public class EDot extends AExpression {
             DotNode dotNode = new DotNode();
             DotNode dotNode = new DotNode();
             dotNode.setLeftNode(prefixOutput.expressionNode);
             dotNode.setLeftNode(prefixOutput.expressionNode);
             dotNode.setRightNode(expressionNode);
             dotNode.setRightNode(expressionNode);
-            dotNode.setLocation(location);
+            dotNode.setLocation(getLocation());
             dotNode.setExpressionType(output.actual);
             dotNode.setExpressionType(output.actual);
             output.expressionNode = dotNode;
             output.expressionNode = dotNode;
         }
         }

+ 23 - 18
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EElvis.java

@@ -35,14 +35,22 @@ import static java.util.Objects.requireNonNull;
  */
  */
 public class EElvis extends AExpression {
 public class EElvis extends AExpression {
 
 
-    protected AExpression lhs;
-    protected AExpression rhs;
+    private final AExpression leftNode;
+    private final AExpression rightNode;
 
 
-    public EElvis(Location location, AExpression lhs, AExpression rhs) {
-        super(location);
+    public EElvis(int identifier, Location location, AExpression leftNode, AExpression rightNode) {
+        super(identifier, location);
 
 
-        this.lhs = requireNonNull(lhs);
-        this.rhs = requireNonNull(rhs);
+        this.leftNode = requireNonNull(leftNode);
+        this.rightNode = requireNonNull(rightNode);
+    }
+
+    public AExpression getLeftNode() {
+        return leftNode;
+    }
+
+    public AExpression getRightNode() {
+        return rightNode;
     }
     }
 
 
     @Override
     @Override
@@ -65,30 +73,27 @@ public class EElvis extends AExpression {
         leftInput.expected = input.expected;
         leftInput.expected = input.expected;
         leftInput.explicit = input.explicit;
         leftInput.explicit = input.explicit;
         leftInput.internal = input.internal;
         leftInput.internal = input.internal;
-        Output leftOutput = analyze(lhs, classNode, scriptRoot, scope, leftInput);
+        Output leftOutput = analyze(leftNode, classNode, scriptRoot, scope, leftInput);
 
 
         Input rightInput = new Input();
         Input rightInput = new Input();
         rightInput.expected = input.expected;
         rightInput.expected = input.expected;
         rightInput.explicit = input.explicit;
         rightInput.explicit = input.explicit;
         rightInput.internal = input.internal;
         rightInput.internal = input.internal;
-        Output rightOutput = analyze(rhs, classNode, scriptRoot, scope, rightInput);
+        Output rightOutput = analyze(rightNode, classNode, scriptRoot, scope, rightInput);
 
 
         output.actual = input.expected;
         output.actual = input.expected;
 
 
-        if (lhs.getChildIf(ENull.class) != null) {
+        if (leftNode instanceof ENull) {
             throw createError(new IllegalArgumentException("Extraneous elvis operator. LHS is null."));
             throw createError(new IllegalArgumentException("Extraneous elvis operator. LHS is null."));
         }
         }
-        if (lhs.getChildIf(EBoolean.class) != null
-                || lhs.getChildIf(ENumeric.class) != null
-                || lhs.getChildIf(EDecimal.class) != null
-                || lhs.getChildIf(EString.class) != null
-                || lhs.getChildIf(EConstant.class) != null) {
+        if (leftNode instanceof EBoolean || leftNode instanceof ENumeric || leftNode instanceof EDecimal
+                || leftNode instanceof EString || leftNode instanceof EConstant) {
             throw createError(new IllegalArgumentException("Extraneous elvis operator. LHS is a constant."));
             throw createError(new IllegalArgumentException("Extraneous elvis operator. LHS is a constant."));
         }
         }
         if (leftOutput.actual.isPrimitive()) {
         if (leftOutput.actual.isPrimitive()) {
             throw createError(new IllegalArgumentException("Extraneous elvis operator. LHS is a primitive."));
             throw createError(new IllegalArgumentException("Extraneous elvis operator. LHS is a primitive."));
         }
         }
-        if (rhs.getChildIf(ENull.class) != null) {
+        if (rightNode instanceof ENull) {
             throw createError(new IllegalArgumentException("Extraneous elvis operator. RHS is null."));
             throw createError(new IllegalArgumentException("Extraneous elvis operator. RHS is null."));
         }
         }
 
 
@@ -100,9 +105,9 @@ public class EElvis extends AExpression {
             output.actual = promote;
             output.actual = promote;
         }
         }
 
 
-        PainlessCast leftCast = AnalyzerCaster.getLegalCast(lhs.location,
+        PainlessCast leftCast = AnalyzerCaster.getLegalCast(leftNode.getLocation(),
                 leftOutput.actual, leftInput.expected, leftInput.explicit, leftInput.internal);
                 leftOutput.actual, leftInput.expected, leftInput.explicit, leftInput.internal);
-        PainlessCast rightCast = AnalyzerCaster.getLegalCast(rhs.location,
+        PainlessCast rightCast = AnalyzerCaster.getLegalCast(rightNode.getLocation(),
                 rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
                 rightOutput.actual, rightInput.expected, rightInput.explicit, rightInput.internal);
 
 
         ElvisNode elvisNode = new ElvisNode();
         ElvisNode elvisNode = new ElvisNode();
@@ -110,7 +115,7 @@ public class EElvis extends AExpression {
         elvisNode.setLeftNode(cast(leftOutput.expressionNode, leftCast));
         elvisNode.setLeftNode(cast(leftOutput.expressionNode, leftCast));
         elvisNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
         elvisNode.setRightNode(cast(rightOutput.expressionNode, rightCast));
 
 
-        elvisNode.setLocation(location);
+        elvisNode.setLocation(getLocation());
         elvisNode.setExpressionType(output.actual);
         elvisNode.setExpressionType(output.actual);
 
 
         output.expressionNode = elvisNode;
         output.expressionNode = elvisNode;

+ 15 - 7
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java

@@ -33,14 +33,22 @@ import java.util.Objects;
  */
  */
 public class EExplicit extends AExpression {
 public class EExplicit extends AExpression {
 
 
-    protected final DType type;
-    protected final AExpression child;
+    private final DType type;
+    private final AExpression childNode;
 
 
-    public EExplicit(Location location, DType type, AExpression child) {
-        super(location);
+    public EExplicit(int identifier, Location location, DType type, AExpression childNode) {
+        super(identifier, location);
 
 
         this.type = Objects.requireNonNull(type);
         this.type = Objects.requireNonNull(type);
-        this.child = Objects.requireNonNull(child);
+        this.childNode = Objects.requireNonNull(childNode);
+    }
+
+    public DType getType() {
+        return type;
+    }
+
+    public AExpression getChildNode() {
+        return childNode;
     }
     }
 
 
     @Override
     @Override
@@ -63,8 +71,8 @@ public class EExplicit extends AExpression {
         Input childInput = new Input();
         Input childInput = new Input();
         childInput.expected = output.actual;
         childInput.expected = output.actual;
         childInput.explicit = true;
         childInput.explicit = true;
-        Output childOutput = analyze(child, classNode, scriptRoot, scope, childInput);
-        PainlessCast childCast = AnalyzerCaster.getLegalCast(child.location,
+        Output childOutput = analyze(childNode, classNode, scriptRoot, scope, childInput);
+        PainlessCast childCast = AnalyzerCaster.getLegalCast(childNode.getLocation(),
                 childOutput.actual, childInput.expected, childInput.explicit, childInput.internal);
                 childOutput.actual, childInput.expected, childInput.explicit, childInput.internal);
 
 
         output.expressionNode = cast(childOutput.expressionNode, childCast);
         output.expressionNode = cast(childOutput.expressionNode, childCast);

+ 34 - 26
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java

@@ -36,50 +36,58 @@ import java.util.Objects;
  */
  */
 public class EFunctionRef extends AExpression {
 public class EFunctionRef extends AExpression {
 
 
-    protected final String name;
-    protected final String call;
+    private final String symbol;
+    private final String methodName;
 
 
-    public EFunctionRef(Location location, String name, String call) {
-        super(location);
+    public EFunctionRef(int identifier, Location location, String symbol, String methodName) {
+        super(identifier, location);
 
 
-        this.name = Objects.requireNonNull(name);
-        this.call = Objects.requireNonNull(call);
+        this.symbol = Objects.requireNonNull(symbol);
+        this.methodName = Objects.requireNonNull(methodName);
+    }
+
+    public String getSymbol() {
+        return symbol;
+    }
+
+    public String getCall() {
+        return methodName;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         Output output = new Output();
         Output output = new Output();
-        Class<?> type = scriptRoot.getPainlessLookup().canonicalTypeNameToType(name);
+        Class<?> type = scriptRoot.getPainlessLookup().canonicalTypeNameToType(symbol);
 
 
-        if (name.equals("this") || type != null)  {
+        if (symbol.equals("this") || type != null)  {
             if (input.write) {
             if (input.write) {
                 throw createError(new IllegalArgumentException(
                 throw createError(new IllegalArgumentException(
-                        "invalid assignment: cannot assign a value to function reference [" + name + ":" + call + "]"));
+                        "invalid assignment: cannot assign a value to function reference [" + symbol + ":" + methodName + "]"));
             }
             }
 
 
             if (input.read == false) {
             if (input.read == false) {
                 throw createError(new IllegalArgumentException(
                 throw createError(new IllegalArgumentException(
-                        "not a statement: function reference [" + name + ":" + call + "] not used"));
+                        "not a statement: function reference [" + symbol + ":" + methodName + "] not used"));
             }
             }
 
 
             if (input.expected == null) {
             if (input.expected == null) {
                 output.actual = String.class;
                 output.actual = String.class;
-                String defReferenceEncoding = "S" + name + "." + call + ",0";
+                String defReferenceEncoding = "S" + symbol + "." + methodName + ",0";
 
 
                 DefInterfaceReferenceNode defInterfaceReferenceNode = new DefInterfaceReferenceNode();
                 DefInterfaceReferenceNode defInterfaceReferenceNode = new DefInterfaceReferenceNode();
 
 
-                defInterfaceReferenceNode.setLocation(location);
+                defInterfaceReferenceNode.setLocation(getLocation());
                 defInterfaceReferenceNode.setExpressionType(output.actual);
                 defInterfaceReferenceNode.setExpressionType(output.actual);
                 defInterfaceReferenceNode.setDefReferenceEncoding(defReferenceEncoding);
                 defInterfaceReferenceNode.setDefReferenceEncoding(defReferenceEncoding);
 
 
                 output.expressionNode = defInterfaceReferenceNode;
                 output.expressionNode = defInterfaceReferenceNode;
             } else {
             } else {
-                FunctionRef ref = FunctionRef.create(
-                        scriptRoot.getPainlessLookup(), scriptRoot.getFunctionTable(), location, input.expected, name, call, 0);
+                FunctionRef ref = FunctionRef.create(scriptRoot.getPainlessLookup(), scriptRoot.getFunctionTable(),
+                        getLocation(), input.expected, symbol, methodName, 0);
                 output.actual = input.expected;
                 output.actual = input.expected;
 
 
                 TypedInterfaceReferenceNode typedInterfaceReferenceNode = new TypedInterfaceReferenceNode();
                 TypedInterfaceReferenceNode typedInterfaceReferenceNode = new TypedInterfaceReferenceNode();
-                typedInterfaceReferenceNode.setLocation(location);
+                typedInterfaceReferenceNode.setLocation(getLocation());
                 typedInterfaceReferenceNode.setExpressionType(output.actual);
                 typedInterfaceReferenceNode.setExpressionType(output.actual);
                 typedInterfaceReferenceNode.setReference(ref);
                 typedInterfaceReferenceNode.setReference(ref);
 
 
@@ -88,29 +96,29 @@ public class EFunctionRef extends AExpression {
         } else {
         } else {
             if (input.write) {
             if (input.write) {
                 throw createError(new IllegalArgumentException(
                 throw createError(new IllegalArgumentException(
-                        "invalid assignment: cannot assign a value to capturing function reference [" + name + ":"  + call + "]"));
+                        "invalid assignment: cannot assign a value to capturing function reference [" + symbol + ":"  + methodName + "]"));
             }
             }
 
 
             if (input.read == false) {
             if (input.read == false) {
                 throw createError(new IllegalArgumentException(
                 throw createError(new IllegalArgumentException(
-                        "not a statement: capturing function reference [" + name + ":"  + call + "] not used"));
+                        "not a statement: capturing function reference [" + symbol + ":"  + methodName + "] not used"));
             }
             }
 
 
-            Scope.Variable captured = scope.getVariable(location, name);
+            Scope.Variable captured = scope.getVariable(getLocation(), symbol);
             if (input.expected == null) {
             if (input.expected == null) {
                 String defReferenceEncoding;
                 String defReferenceEncoding;
                 if (captured.getType() == def.class) {
                 if (captured.getType() == def.class) {
                     // dynamic implementation
                     // dynamic implementation
-                    defReferenceEncoding = "D" + name + "." + call + ",1";
+                    defReferenceEncoding = "D" + symbol + "." + methodName + ",1";
                 } else {
                 } else {
                     // typed implementation
                     // typed implementation
-                    defReferenceEncoding = "S" + captured.getCanonicalTypeName() + "." + call + ",1";
+                    defReferenceEncoding = "S" + captured.getCanonicalTypeName() + "." + methodName + ",1";
                 }
                 }
                 output.actual = String.class;
                 output.actual = String.class;
 
 
                 DefInterfaceReferenceNode defInterfaceReferenceNode = new DefInterfaceReferenceNode();
                 DefInterfaceReferenceNode defInterfaceReferenceNode = new DefInterfaceReferenceNode();
 
 
-                defInterfaceReferenceNode.setLocation(location);
+                defInterfaceReferenceNode.setLocation(getLocation());
                 defInterfaceReferenceNode.setExpressionType(output.actual);
                 defInterfaceReferenceNode.setExpressionType(output.actual);
                 defInterfaceReferenceNode.addCapture(captured.getName());
                 defInterfaceReferenceNode.addCapture(captured.getName());
                 defInterfaceReferenceNode.setDefReferenceEncoding(defReferenceEncoding);
                 defInterfaceReferenceNode.setDefReferenceEncoding(defReferenceEncoding);
@@ -120,11 +128,11 @@ public class EFunctionRef extends AExpression {
                 output.actual = input.expected;
                 output.actual = input.expected;
                 // static case
                 // static case
                 if (captured.getType() != def.class) {
                 if (captured.getType() != def.class) {
-                    FunctionRef ref = FunctionRef.create(scriptRoot.getPainlessLookup(), scriptRoot.getFunctionTable(), location,
-                            input.expected, captured.getCanonicalTypeName(), call, 1);
+                    FunctionRef ref = FunctionRef.create(scriptRoot.getPainlessLookup(), scriptRoot.getFunctionTable(), getLocation(),
+                            input.expected, captured.getCanonicalTypeName(), methodName, 1);
 
 
                     TypedInterfaceReferenceNode typedInterfaceReferenceNode = new TypedInterfaceReferenceNode();
                     TypedInterfaceReferenceNode typedInterfaceReferenceNode = new TypedInterfaceReferenceNode();
-                    typedInterfaceReferenceNode.setLocation(location);
+                    typedInterfaceReferenceNode.setLocation(getLocation());
                     typedInterfaceReferenceNode.setExpressionType(output.actual);
                     typedInterfaceReferenceNode.setExpressionType(output.actual);
                     typedInterfaceReferenceNode.addCapture(captured.getName());
                     typedInterfaceReferenceNode.addCapture(captured.getName());
                     typedInterfaceReferenceNode.setReference(ref);
                     typedInterfaceReferenceNode.setReference(ref);
@@ -132,10 +140,10 @@ public class EFunctionRef extends AExpression {
                     output.expressionNode = typedInterfaceReferenceNode;
                     output.expressionNode = typedInterfaceReferenceNode;
                 } else {
                 } else {
                     TypedCaptureReferenceNode typedCaptureReferenceNode = new TypedCaptureReferenceNode();
                     TypedCaptureReferenceNode typedCaptureReferenceNode = new TypedCaptureReferenceNode();
-                    typedCaptureReferenceNode.setLocation(location);
+                    typedCaptureReferenceNode.setLocation(getLocation());
                     typedCaptureReferenceNode.setExpressionType(output.actual);
                     typedCaptureReferenceNode.setExpressionType(output.actual);
                     typedCaptureReferenceNode.addCapture(captured.getName());
                     typedCaptureReferenceNode.addCapture(captured.getName());
-                    typedCaptureReferenceNode.setMethodName(call);
+                    typedCaptureReferenceNode.setMethodName(methodName);
 
 
                     output.expressionNode = typedCaptureReferenceNode;
                     output.expressionNode = typedCaptureReferenceNode;
                 }
                 }

+ 22 - 13
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java

@@ -37,25 +37,34 @@ import java.util.Objects;
  */
  */
 public class EInstanceof extends AExpression {
 public class EInstanceof extends AExpression {
 
 
-    protected final AExpression expression;
-    protected final String type;
+    private final AExpression expressionNode;
+    private final String canonicalTypeName;
 
 
-    public EInstanceof(Location location, AExpression expression, String type) {
-        super(location);
-        this.expression = Objects.requireNonNull(expression);
-        this.type = Objects.requireNonNull(type);
+    public EInstanceof(int identifier, Location location, AExpression expression, String canonicalTypeName) {
+        super(identifier, location);
+
+        this.expressionNode = Objects.requireNonNull(expression);
+        this.canonicalTypeName = Objects.requireNonNull(canonicalTypeName);
+    }
+
+    public AExpression getExpressionNode() {
+        return expressionNode;
+    }
+
+    public String getCanonicalTypeName() {
+        return canonicalTypeName;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         if (input.write) {
         if (input.write) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "invalid assignment: cannot assign a value to instanceof with target type [" + type + "]"));
+                    "invalid assignment: cannot assign a value to instanceof with target type [" + canonicalTypeName + "]"));
         }
         }
 
 
         if (input.read == false) {
         if (input.read == false) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "not a statement: result not used from instanceof with target type [" + type + "]"));
+                    "not a statement: result not used from instanceof with target type [" + canonicalTypeName + "]"));
         }
         }
 
 
         Class<?> resolvedType;
         Class<?> resolvedType;
@@ -65,10 +74,10 @@ public class EInstanceof extends AExpression {
         Output output = new Output();
         Output output = new Output();
 
 
         // ensure the specified type is part of the definition
         // ensure the specified type is part of the definition
-        Class<?> clazz = scriptRoot.getPainlessLookup().canonicalTypeNameToType(this.type);
+        Class<?> clazz = scriptRoot.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
 
 
         if (clazz == null) {
         if (clazz == null) {
-            throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
+            throw createError(new IllegalArgumentException("Not a type [" + canonicalTypeName + "]."));
         }
         }
 
 
         // map to wrapped type for primitive types
         // map to wrapped type for primitive types
@@ -77,9 +86,9 @@ public class EInstanceof extends AExpression {
 
 
         // analyze and cast the expression
         // analyze and cast the expression
         Input expressionInput = new Input();
         Input expressionInput = new Input();
-        Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
+        Output expressionOutput = analyze(expressionNode, classNode, scriptRoot, scope, expressionInput);
         expressionInput.expected = expressionOutput.actual;
         expressionInput.expected = expressionOutput.actual;
-        PainlessCast expressionCast = AnalyzerCaster.getLegalCast(expression.location,
+        PainlessCast expressionCast = AnalyzerCaster.getLegalCast(expressionNode.getLocation(),
                 expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal);
                 expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal);
 
 
         // record if the expression returns a primitive
         // record if the expression returns a primitive
@@ -94,7 +103,7 @@ public class EInstanceof extends AExpression {
 
 
         instanceofNode.setChildNode(cast(expressionOutput.expressionNode, expressionCast));
         instanceofNode.setChildNode(cast(expressionOutput.expressionNode, expressionCast));
 
 
-        instanceofNode.setLocation(location);
+        instanceofNode.setLocation(getLocation());
         instanceofNode.setExpressionType(output.actual);
         instanceofNode.setExpressionType(output.actual);
         instanceofNode.setInstanceType(expressionType);
         instanceofNode.setInstanceType(expressionType);
         instanceofNode.setResolvedType(resolvedType);
         instanceofNode.setResolvedType(resolvedType);

+ 37 - 23
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java

@@ -65,16 +65,30 @@ import java.util.Objects;
  */
  */
 public class ELambda extends AExpression {
 public class ELambda extends AExpression {
 
 
-    protected final List<String> paramTypeStrs;
-    protected final List<String> paramNameStrs;
-    protected final SBlock block;
+    private final List<String> canonicalTypeNameParameters;
+    private final List<String> parameterNames;
+    private final SBlock blockNode;
 
 
-    public ELambda(Location location, List<String> paramTypes, List<String> paramNames, SBlock block) {
-        super(location);
-        this.paramTypeStrs = Collections.unmodifiableList(paramTypes);
-        this.paramNameStrs = Collections.unmodifiableList(paramNames);
-        this.block = Objects.requireNonNull(block);
+    public ELambda(int identifier, Location location,
+            List<String> canonicalTypeNameParameters, List<String> parameterNames, SBlock blockNode) {
 
 
+        super(identifier, location);
+
+        this.canonicalTypeNameParameters = Collections.unmodifiableList(Objects.requireNonNull(canonicalTypeNameParameters));
+        this.parameterNames = Collections.unmodifiableList(Objects.requireNonNull(parameterNames));
+        this.blockNode = Objects.requireNonNull(blockNode);
+    }
+
+    public List<String> getCanonicalTypeNameParameters() {
+        return canonicalTypeNameParameters;
+    }
+
+    public List<String> getParameterNames() {
+        return parameterNames;
+    }
+
+    public SBlock getBlockNode() {
+        return blockNode;
     }
     }
 
 
     @Override
     @Override
@@ -103,8 +117,8 @@ public class ELambda extends AExpression {
             // we don't know anything: treat as def
             // we don't know anything: treat as def
             returnType = def.class;
             returnType = def.class;
             // don't infer any types, replace any null types with def
             // don't infer any types, replace any null types with def
-            typeParameters = new ArrayList<>(paramTypeStrs.size());
-            for (String type : paramTypeStrs) {
+            typeParameters = new ArrayList<>(canonicalTypeNameParameters.size());
+            for (String type : canonicalTypeNameParameters) {
                 if (type == null) {
                 if (type == null) {
                     typeParameters.add(def.class);
                     typeParameters.add(def.class);
                 } else {
                 } else {
@@ -126,7 +140,7 @@ public class ELambda extends AExpression {
                         "[" + PainlessLookupUtility.typeToCanonicalTypeName(input.expected) + "], not a functional interface"));
                         "[" + PainlessLookupUtility.typeToCanonicalTypeName(input.expected) + "], not a functional interface"));
             }
             }
             // check arity before we manipulate parameters
             // check arity before we manipulate parameters
-            if (interfaceMethod.typeParameters.size() != paramTypeStrs.size())
+            if (interfaceMethod.typeParameters.size() != canonicalTypeNameParameters.size())
                 throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.javaMethod.getName() +
                 throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.javaMethod.getName() +
                         "] in [" + PainlessLookupUtility.typeToCanonicalTypeName(input.expected) + "]");
                         "] in [" + PainlessLookupUtility.typeToCanonicalTypeName(input.expected) + "]");
             // for method invocation, its allowed to ignore the return value
             // for method invocation, its allowed to ignore the return value
@@ -136,9 +150,9 @@ public class ELambda extends AExpression {
                 returnType = interfaceMethod.returnType;
                 returnType = interfaceMethod.returnType;
             }
             }
             // replace any null types with the actual type
             // replace any null types with the actual type
-            typeParameters = new ArrayList<>(paramTypeStrs.size());
-            for (int i = 0; i < paramTypeStrs.size(); i++) {
-                String paramType = paramTypeStrs.get(i);
+            typeParameters = new ArrayList<>(canonicalTypeNameParameters.size());
+            for (int i = 0; i < canonicalTypeNameParameters.size(); i++) {
+                String paramType = canonicalTypeNameParameters.get(i);
                 if (paramType == null) {
                 if (paramType == null) {
                     typeParameters.add(interfaceMethod.typeParameters.get(i));
                     typeParameters.add(interfaceMethod.typeParameters.get(i));
                 } else {
                 } else {
@@ -157,16 +171,16 @@ public class ELambda extends AExpression {
 
 
         for (int index = 0; index < typeParameters.size(); ++index) {
         for (int index = 0; index < typeParameters.size(); ++index) {
             Class<?> type = typeParameters.get(index);
             Class<?> type = typeParameters.get(index);
-            String paramName = paramNameStrs.get(index);
-            lambdaScope.defineVariable(location, type, paramName, true);
+            String paramName = this.parameterNames.get(index);
+            lambdaScope.defineVariable(getLocation(), type, paramName, true);
         }
         }
 
 
-        if (block.statements.isEmpty()) {
+        if (blockNode.getStatementNodes().isEmpty()) {
             throw createError(new IllegalArgumentException("cannot generate empty lambda"));
             throw createError(new IllegalArgumentException("cannot generate empty lambda"));
         }
         }
         AStatement.Input blockInput = new AStatement.Input();
         AStatement.Input blockInput = new AStatement.Input();
         blockInput.lastSource = true;
         blockInput.lastSource = true;
-        AStatement.Output blockOutput = block.analyze(classNode, scriptRoot, lambdaScope, blockInput);
+        AStatement.Output blockOutput = blockNode.analyze(classNode, scriptRoot, lambdaScope, blockInput);
 
 
         if (blockOutput.methodEscape == false) {
         if (blockOutput.methodEscape == false) {
             throw createError(new IllegalArgumentException("not all paths return a value for lambda"));
             throw createError(new IllegalArgumentException("not all paths return a value for lambda"));
@@ -177,13 +191,13 @@ public class ELambda extends AExpression {
         // prepend capture list to lambda's arguments
         // prepend capture list to lambda's arguments
         List<Variable> captures = new ArrayList<>(lambdaScope.getCaptures());
         List<Variable> captures = new ArrayList<>(lambdaScope.getCaptures());
         typeParametersWithCaptures = new ArrayList<>(captures.size() + typeParameters.size());
         typeParametersWithCaptures = new ArrayList<>(captures.size() + typeParameters.size());
-        parameterNames = new ArrayList<>(captures.size() + paramNameStrs.size());
+        parameterNames = new ArrayList<>(captures.size() + this.parameterNames.size());
         for (Variable var : captures) {
         for (Variable var : captures) {
             typeParametersWithCaptures.add(var.getType());
             typeParametersWithCaptures.add(var.getType());
             parameterNames.add(var.getName());
             parameterNames.add(var.getName());
         }
         }
         typeParametersWithCaptures.addAll(typeParameters);
         typeParametersWithCaptures.addAll(typeParameters);
-        parameterNames.addAll(paramNameStrs);
+        parameterNames.addAll(this.parameterNames);
 
 
         // desugar lambda body into a synthetic method
         // desugar lambda body into a synthetic method
         name = scriptRoot.getNextSyntheticName("lambda");
         name = scriptRoot.getNextSyntheticName("lambda");
@@ -201,7 +215,7 @@ public class ELambda extends AExpression {
             referenceNode = defInterfaceReferenceNode;
             referenceNode = defInterfaceReferenceNode;
         } else {
         } else {
             FunctionRef ref = FunctionRef.create(scriptRoot.getPainlessLookup(), scriptRoot.getFunctionTable(),
             FunctionRef ref = FunctionRef.create(scriptRoot.getPainlessLookup(), scriptRoot.getFunctionTable(),
-                    location, input.expected, "this", name, captures.size());
+                    getLocation(), input.expected, "this", name, captures.size());
             output.actual = input.expected;
             output.actual = input.expected;
 
 
             TypedInterfaceReferenceNode typedInterfaceReferenceNode = new TypedInterfaceReferenceNode();
             TypedInterfaceReferenceNode typedInterfaceReferenceNode = new TypedInterfaceReferenceNode();
@@ -211,7 +225,7 @@ public class ELambda extends AExpression {
 
 
         FunctionNode functionNode = new FunctionNode();
         FunctionNode functionNode = new FunctionNode();
         functionNode.setBlockNode((BlockNode)blockOutput.statementNode);
         functionNode.setBlockNode((BlockNode)blockOutput.statementNode);
-        functionNode.setLocation(location);
+        functionNode.setLocation(getLocation());
         functionNode.setName(name);
         functionNode.setName(name);
         functionNode.setReturnType(returnType);
         functionNode.setReturnType(returnType);
         functionNode.getTypeParameters().addAll(typeParametersWithCaptures);
         functionNode.getTypeParameters().addAll(typeParametersWithCaptures);
@@ -223,7 +237,7 @@ public class ELambda extends AExpression {
 
 
         classNode.addFunctionNode(functionNode);
         classNode.addFunctionNode(functionNode);
 
 
-        referenceNode.setLocation(location);
+        referenceNode.setLocation(getLocation());
         referenceNode.setExpressionType(output.actual);
         referenceNode.setExpressionType(output.actual);
 
 
         for (Variable capture : captures) {
         for (Variable capture : captures) {

+ 14 - 10
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java

@@ -42,12 +42,16 @@ import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCano
  */
  */
 public class EListInit extends AExpression {
 public class EListInit extends AExpression {
 
 
-    protected final List<AExpression> values;
+    private final List<AExpression> valueNodes;
 
 
-    public EListInit(Location location, List<AExpression> values) {
-        super(location);
+    public EListInit(int identifier, Location location, List<AExpression> valueNodes) {
+        super(identifier, location);
 
 
-        this.values = Collections.unmodifiableList(Objects.requireNonNull(values));
+        this.valueNodes = Collections.unmodifiableList(Objects.requireNonNull(valueNodes));
+    }
+
+    public List<AExpression> getValueNodes() {
+        return valueNodes;
     }
     }
 
 
     @Override
     @Override
@@ -76,26 +80,26 @@ public class EListInit extends AExpression {
             throw createError(new IllegalArgumentException("method [" + typeToCanonicalTypeName(output.actual) + ", add/1] not found"));
             throw createError(new IllegalArgumentException("method [" + typeToCanonicalTypeName(output.actual) + ", add/1] not found"));
         }
         }
 
 
-        List<Output> valueOutputs = new ArrayList<>(values.size());
-        List<PainlessCast> valueCasts = new ArrayList<>(values.size());
+        List<Output> valueOutputs = new ArrayList<>(valueNodes.size());
+        List<PainlessCast> valueCasts = new ArrayList<>(valueNodes.size());
 
 
-        for (AExpression expression : values) {
+        for (AExpression expression : valueNodes) {
             Input expressionInput = new Input();
             Input expressionInput = new Input();
             expressionInput.expected = def.class;
             expressionInput.expected = def.class;
             expressionInput.internal = true;
             expressionInput.internal = true;
             Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
             Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
             valueOutputs.add(expressionOutput);
             valueOutputs.add(expressionOutput);
-            valueCasts.add(AnalyzerCaster.getLegalCast(expression.location,
+            valueCasts.add(AnalyzerCaster.getLegalCast(expression.getLocation(),
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
         }
         }
 
 
         ListInitializationNode listInitializationNode = new ListInitializationNode();
         ListInitializationNode listInitializationNode = new ListInitializationNode();
 
 
-        for (int i = 0; i < values.size(); ++i) {
+        for (int i = 0; i < valueNodes.size(); ++i) {
             listInitializationNode.addArgumentNode(cast(valueOutputs.get(i).expressionNode, valueCasts.get(i)));
             listInitializationNode.addArgumentNode(cast(valueOutputs.get(i).expressionNode, valueCasts.get(i)));
         }
         }
 
 
-        listInitializationNode.setLocation(location);
+        listInitializationNode.setLocation(getLocation());
         listInitializationNode.setExpressionType(output.actual);
         listInitializationNode.setExpressionType(output.actual);
         listInitializationNode.setConstructor(constructor);
         listInitializationNode.setConstructor(constructor);
         listInitializationNode.setMethod(method);
         listInitializationNode.setMethod(method);

+ 26 - 18
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java

@@ -43,14 +43,22 @@ import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCano
  */
  */
 public class EMapInit extends AExpression {
 public class EMapInit extends AExpression {
 
 
-    protected final List<AExpression> keys;
-    protected final List<AExpression> values;
+    private final List<AExpression> keyNodes;
+    private final List<AExpression> valueNodes;
 
 
-    public EMapInit(Location location, List<AExpression> keys, List<AExpression> values) {
-        super(location);
+    public EMapInit(int identifier, Location location, List<AExpression> keyNodes, List<AExpression> valueNodes) {
+        super(identifier, location);
 
 
-        this.keys = Collections.unmodifiableList(Objects.requireNonNull(keys));
-        this.values = Collections.unmodifiableList(Objects.requireNonNull(values));
+        this.keyNodes = Collections.unmodifiableList(Objects.requireNonNull(keyNodes));
+        this.valueNodes = Collections.unmodifiableList(Objects.requireNonNull(valueNodes));
+    }
+
+    public List<AExpression> getKeyNodes() {
+        return keyNodes;
+    }
+
+    public List<AExpression> getValueNodes() {
+        return valueNodes;
     }
     }
 
 
     @Override
     @Override
@@ -79,31 +87,31 @@ public class EMapInit extends AExpression {
             throw createError(new IllegalArgumentException("method [" + typeToCanonicalTypeName(output.actual) + ", put/2] not found"));
             throw createError(new IllegalArgumentException("method [" + typeToCanonicalTypeName(output.actual) + ", put/2] not found"));
         }
         }
 
 
-        if (keys.size() != values.size()) {
+        if (keyNodes.size() != valueNodes.size()) {
             throw createError(new IllegalStateException("Illegal tree structure."));
             throw createError(new IllegalStateException("Illegal tree structure."));
         }
         }
 
 
-        List<Output> keyOutputs = new ArrayList<>(keys.size());
-        List<PainlessCast> keyCasts = new ArrayList<>(keys.size());
-        List<Output> valueOutputs = new ArrayList<>(values.size());
-        List<PainlessCast> valueCasts = new ArrayList<>(values.size());
+        List<Output> keyOutputs = new ArrayList<>(keyNodes.size());
+        List<PainlessCast> keyCasts = new ArrayList<>(keyNodes.size());
+        List<Output> valueOutputs = new ArrayList<>(valueNodes.size());
+        List<PainlessCast> valueCasts = new ArrayList<>(valueNodes.size());
 
 
-        for (int i = 0; i < keys.size(); ++i) {
-            AExpression expression = keys.get(i);
+        for (int i = 0; i < keyNodes.size(); ++i) {
+            AExpression expression = keyNodes.get(i);
             Input expressionInput = new Input();
             Input expressionInput = new Input();
             expressionInput.expected = def.class;
             expressionInput.expected = def.class;
             expressionInput.internal = true;
             expressionInput.internal = true;
             Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
             Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
             keyOutputs.add(expressionOutput);
             keyOutputs.add(expressionOutput);
-            keyCasts.add(AnalyzerCaster.getLegalCast(expression.location,
+            keyCasts.add(AnalyzerCaster.getLegalCast(expression.getLocation(),
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
 
 
-            expression = values.get(i);
+            expression = valueNodes.get(i);
             expressionInput = new Input();
             expressionInput = new Input();
             expressionInput.expected = def.class;
             expressionInput.expected = def.class;
             expressionInput.internal = true;
             expressionInput.internal = true;
             expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
             expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
-            valueCasts.add(AnalyzerCaster.getLegalCast(expression.location,
+            valueCasts.add(AnalyzerCaster.getLegalCast(expression.getLocation(),
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
 
 
             valueOutputs.add(expressionOutput);
             valueOutputs.add(expressionOutput);
@@ -111,13 +119,13 @@ public class EMapInit extends AExpression {
 
 
         MapInitializationNode mapInitializationNode = new MapInitializationNode();
         MapInitializationNode mapInitializationNode = new MapInitializationNode();
 
 
-        for (int i = 0; i < keys.size(); ++i) {
+        for (int i = 0; i < keyNodes.size(); ++i) {
             mapInitializationNode.addArgumentNode(
             mapInitializationNode.addArgumentNode(
                     cast(keyOutputs.get(i).expressionNode, keyCasts.get(i)),
                     cast(keyOutputs.get(i).expressionNode, keyCasts.get(i)),
                     cast(valueOutputs.get(i).expressionNode, valueCasts.get(i)));
                     cast(valueOutputs.get(i).expressionNode, valueCasts.get(i)));
         }
         }
 
 
-        mapInitializationNode.setLocation(location);
+        mapInitializationNode.setLocation(getLocation());
         mapInitializationNode.setExpressionType(output.actual);
         mapInitializationNode.setExpressionType(output.actual);
         mapInitializationNode.setConstructor(constructor);
         mapInitializationNode.setConstructor(constructor);
         mapInitializationNode.setMethod(method);
         mapInitializationNode.setMethod(method);

+ 28 - 16
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java

@@ -37,16 +37,28 @@ import java.util.Objects;
  */
  */
 public class ENewArray extends AExpression {
 public class ENewArray extends AExpression {
 
 
-    protected final String type;
-    protected final List<AExpression> arguments;
-    protected final boolean initialize;
+    private final String canonicalTypeName;
+    private final List<AExpression> valueNodes;
+    private final boolean isInitializer;
 
 
-    public ENewArray(Location location, String type, List<AExpression> arguments, boolean initialize) {
-        super(location);
+    public ENewArray(int identifier, Location location, String canonicalTypeName, List<AExpression> valueNodes, boolean isInitializer) {
+        super(identifier, location);
 
 
-        this.type = Objects.requireNonNull(type);
-        this.arguments = Collections.unmodifiableList(Objects.requireNonNull(arguments));
-        this.initialize = initialize;
+        this.canonicalTypeName = Objects.requireNonNull(canonicalTypeName);
+        this.valueNodes = Collections.unmodifiableList(Objects.requireNonNull(valueNodes));
+        this.isInitializer = isInitializer;
+    }
+
+    public String getCanonicalTypeName() {
+        return canonicalTypeName;
+    }
+
+    public List<AExpression> getValueNodes() {
+        return valueNodes;
+    }
+
+    public boolean isInitializer() {
+        return isInitializer;
     }
     }
 
 
     @Override
     @Override
@@ -61,22 +73,22 @@ public class ENewArray extends AExpression {
 
 
         Output output = new Output();
         Output output = new Output();
 
 
-        Class<?> clazz = scriptRoot.getPainlessLookup().canonicalTypeNameToType(this.type);
+        Class<?> clazz = scriptRoot.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
 
 
         if (clazz == null) {
         if (clazz == null) {
-            throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
+            throw createError(new IllegalArgumentException("Not a type [" + canonicalTypeName + "]."));
         }
         }
 
 
         List<Output> argumentOutputs = new ArrayList<>();
         List<Output> argumentOutputs = new ArrayList<>();
         List<PainlessCast> argumentCasts = new ArrayList<>();
         List<PainlessCast> argumentCasts = new ArrayList<>();
 
 
-        for (AExpression expression : arguments) {
+        for (AExpression expression : valueNodes) {
             Input expressionInput = new Input();
             Input expressionInput = new Input();
-            expressionInput.expected = initialize ? clazz.getComponentType() : int.class;
+            expressionInput.expected = isInitializer ? clazz.getComponentType() : int.class;
             expressionInput.internal = true;
             expressionInput.internal = true;
             Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
             Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
             argumentOutputs.add(expressionOutput);
             argumentOutputs.add(expressionOutput);
-            argumentCasts.add(AnalyzerCaster.getLegalCast(expression.location,
+            argumentCasts.add(AnalyzerCaster.getLegalCast(expression.getLocation(),
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
         }
         }
 
 
@@ -84,13 +96,13 @@ public class ENewArray extends AExpression {
 
 
         NewArrayNode newArrayNode = new NewArrayNode();
         NewArrayNode newArrayNode = new NewArrayNode();
 
 
-        for (int i = 0; i < arguments.size(); ++ i) {
+        for (int i = 0; i < valueNodes.size(); ++ i) {
             newArrayNode.addArgumentNode(cast(argumentOutputs.get(i).expressionNode, argumentCasts.get(i)));
             newArrayNode.addArgumentNode(cast(argumentOutputs.get(i).expressionNode, argumentCasts.get(i)));
         }
         }
 
 
-        newArrayNode.setLocation(location);
+        newArrayNode.setLocation(getLocation());
         newArrayNode.setExpressionType(output.actual);
         newArrayNode.setExpressionType(output.actual);
-        newArrayNode.setInitialize(initialize);
+        newArrayNode.setInitialize(isInitializer);
 
 
         output.expressionNode = newArrayNode;
         output.expressionNode = newArrayNode;
 
 

+ 14 - 14
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArrayFunctionRef.java

@@ -40,32 +40,32 @@ import java.util.Objects;
  */
  */
 public class ENewArrayFunctionRef extends AExpression {
 public class ENewArrayFunctionRef extends AExpression {
 
 
-    protected final String type;
+    private final String canonicalTypeName;
 
 
-    public ENewArrayFunctionRef(Location location, String type) {
-        super(location);
+    public ENewArrayFunctionRef(int identifier, Location location, String canonicalTypeName) {
+        super(identifier, location);
 
 
-        this.type = Objects.requireNonNull(type);
+        this.canonicalTypeName = Objects.requireNonNull(canonicalTypeName);
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         if (input.write) {
         if (input.write) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "cannot assign a value to new array function reference with target type [ + " + type  + "]"));
+                    "cannot assign a value to new array function reference with target type [ + " + canonicalTypeName  + "]"));
         }
         }
 
 
         if (input.read == false) {
         if (input.read == false) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "not a statement: new array function reference with target type [" + type + "] not used"));
+                    "not a statement: new array function reference with target type [" + canonicalTypeName + "] not used"));
         }
         }
 
 
         Output output = new Output();
         Output output = new Output();
 
 
-        Class<?> clazz = scriptRoot.getPainlessLookup().canonicalTypeNameToType(this.type);
+        Class<?> clazz = scriptRoot.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
 
 
         if (clazz == null) {
         if (clazz == null) {
-            throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
+            throw createError(new IllegalArgumentException("Not a type [" + canonicalTypeName + "]."));
         }
         }
 
 
         String name = scriptRoot.getNextSyntheticName("newarray");
         String name = scriptRoot.getNextSyntheticName("newarray");
@@ -77,19 +77,19 @@ public class ENewArrayFunctionRef extends AExpression {
 
 
             DefInterfaceReferenceNode defInterfaceReferenceNode = new DefInterfaceReferenceNode();
             DefInterfaceReferenceNode defInterfaceReferenceNode = new DefInterfaceReferenceNode();
 
 
-            defInterfaceReferenceNode.setLocation(location);
+            defInterfaceReferenceNode.setLocation(getLocation());
             defInterfaceReferenceNode.setExpressionType(output.actual);
             defInterfaceReferenceNode.setExpressionType(output.actual);
             defInterfaceReferenceNode.setDefReferenceEncoding(defReferenceEncoding);
             defInterfaceReferenceNode.setDefReferenceEncoding(defReferenceEncoding);
 
 
             output.expressionNode = defInterfaceReferenceNode;
             output.expressionNode = defInterfaceReferenceNode;
         } else {
         } else {
             FunctionRef ref = FunctionRef.create(scriptRoot.getPainlessLookup(), scriptRoot.getFunctionTable(),
             FunctionRef ref = FunctionRef.create(scriptRoot.getPainlessLookup(), scriptRoot.getFunctionTable(),
-                    location, input.expected, "this", name, 0);
+                    getLocation(), input.expected, "this", name, 0);
             output.actual = input.expected;
             output.actual = input.expected;
 
 
             TypedInterfaceReferenceNode typedInterfaceReferenceNode = new TypedInterfaceReferenceNode();
             TypedInterfaceReferenceNode typedInterfaceReferenceNode = new TypedInterfaceReferenceNode();
 
 
-            typedInterfaceReferenceNode.setLocation(location);
+            typedInterfaceReferenceNode.setLocation(getLocation());
             typedInterfaceReferenceNode.setExpressionType(output.actual);
             typedInterfaceReferenceNode.setExpressionType(output.actual);
             typedInterfaceReferenceNode.setReference(ref);
             typedInterfaceReferenceNode.setReference(ref);
 
 
@@ -97,19 +97,19 @@ public class ENewArrayFunctionRef extends AExpression {
         }
         }
 
 
         VariableNode variableNode = new VariableNode();
         VariableNode variableNode = new VariableNode();
-        variableNode.setLocation(location);
+        variableNode.setLocation(getLocation());
         variableNode.setExpressionType(int.class);
         variableNode.setExpressionType(int.class);
         variableNode.setName("size");
         variableNode.setName("size");
 
 
         NewArrayNode newArrayNode = new NewArrayNode();
         NewArrayNode newArrayNode = new NewArrayNode();
-        newArrayNode.setLocation(location);
+        newArrayNode.setLocation(getLocation());
         newArrayNode.setExpressionType(clazz);
         newArrayNode.setExpressionType(clazz);
         newArrayNode.setInitialize(false);
         newArrayNode.setInitialize(false);
 
 
         newArrayNode.addArgumentNode(variableNode);
         newArrayNode.addArgumentNode(variableNode);
 
 
         ReturnNode returnNode = new ReturnNode();
         ReturnNode returnNode = new ReturnNode();
-        returnNode.setLocation(location);
+        returnNode.setLocation(getLocation());
         returnNode.setExpressionNode(newArrayNode);
         returnNode.setExpressionNode(newArrayNode);
 
 
         BlockNode blockNode = new BlockNode();
         BlockNode blockNode = new BlockNode();

+ 27 - 19
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java

@@ -42,36 +42,44 @@ import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCano
  */
  */
 public class ENewObj extends AExpression {
 public class ENewObj extends AExpression {
 
 
-    protected final String type;
-    protected final List<AExpression> arguments;
+    private final String canonicalTypeName;
+    private final List<AExpression> argumentNodes;
 
 
-    public ENewObj(Location location, String type, List<AExpression> arguments) {
-        super(location);
+    public ENewObj(int identifier, Location location, String canonicalTypeName, List<AExpression> argumentNodes) {
+        super(identifier, location);
 
 
-        this.type = Objects.requireNonNull(type);
-        this.arguments = Collections.unmodifiableList(Objects.requireNonNull(arguments));
+        this.canonicalTypeName = Objects.requireNonNull(canonicalTypeName);
+        this.argumentNodes = Collections.unmodifiableList(Objects.requireNonNull(argumentNodes));
+    }
+
+    public String getCanonicalTypeName() {
+        return canonicalTypeName;
+    }
+
+    public List<AExpression> getArgumentNodes() {
+        return argumentNodes;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         if (input.write) {
         if (input.write) {
-            throw createError(new IllegalArgumentException(
-                    "invalid assignment cannot assign a value to new object with constructor [" + type + "/" + arguments.size() + "]"));
+            throw createError(new IllegalArgumentException("invalid assignment cannot assign a value to new object with constructor " +
+                    "[" + canonicalTypeName + "/" + argumentNodes.size() + "]"));
         }
         }
 
 
         Output output = new Output();
         Output output = new Output();
 
 
-        output.actual = scriptRoot.getPainlessLookup().canonicalTypeNameToType(this.type);
+        output.actual = scriptRoot.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
 
 
         if (output.actual == null) {
         if (output.actual == null) {
-            throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
+            throw createError(new IllegalArgumentException("Not a type [" + canonicalTypeName + "]."));
         }
         }
 
 
-        PainlessConstructor constructor = scriptRoot.getPainlessLookup().lookupPainlessConstructor(output.actual, arguments.size());
+        PainlessConstructor constructor = scriptRoot.getPainlessLookup().lookupPainlessConstructor(output.actual, argumentNodes.size());
 
 
         if (constructor == null) {
         if (constructor == null) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "constructor [" + typeToCanonicalTypeName(output.actual) + ", <init>/" + arguments.size() + "] not found"));
+                    "constructor [" + typeToCanonicalTypeName(output.actual) + ", <init>/" + argumentNodes.size() + "] not found"));
         }
         }
 
 
         scriptRoot.markNonDeterministic(constructor.annotations.containsKey(NonDeterministicAnnotation.class));
         scriptRoot.markNonDeterministic(constructor.annotations.containsKey(NonDeterministicAnnotation.class));
@@ -79,34 +87,34 @@ public class ENewObj extends AExpression {
         Class<?>[] types = new Class<?>[constructor.typeParameters.size()];
         Class<?>[] types = new Class<?>[constructor.typeParameters.size()];
         constructor.typeParameters.toArray(types);
         constructor.typeParameters.toArray(types);
 
 
-        if (constructor.typeParameters.size() != arguments.size()) {
+        if (constructor.typeParameters.size() != argumentNodes.size()) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
                     "When calling constructor on type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "] " +
                     "When calling constructor on type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "] " +
-                    "expected [" + constructor.typeParameters.size() + "] arguments, but found [" + arguments.size() + "]."));
+                    "expected [" + constructor.typeParameters.size() + "] arguments, but found [" + argumentNodes.size() + "]."));
         }
         }
 
 
         List<Output> argumentOutputs = new ArrayList<>();
         List<Output> argumentOutputs = new ArrayList<>();
         List<PainlessCast> argumentCasts = new ArrayList<>();
         List<PainlessCast> argumentCasts = new ArrayList<>();
 
 
-        for (int i = 0; i < arguments.size(); ++i) {
-            AExpression expression = arguments.get(i);
+        for (int i = 0; i < argumentNodes.size(); ++i) {
+            AExpression expression = argumentNodes.get(i);
 
 
             Input expressionInput = new Input();
             Input expressionInput = new Input();
             expressionInput.expected = types[i];
             expressionInput.expected = types[i];
             expressionInput.internal = true;
             expressionInput.internal = true;
             Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
             Output expressionOutput = analyze(expression, classNode, scriptRoot, scope, expressionInput);
             argumentOutputs.add(expressionOutput);
             argumentOutputs.add(expressionOutput);
-            argumentCasts.add(AnalyzerCaster.getLegalCast(expression.location,
+            argumentCasts.add(AnalyzerCaster.getLegalCast(expression.getLocation(),
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal));
         }
         }
 
 
         NewObjectNode newObjectNode = new NewObjectNode();
         NewObjectNode newObjectNode = new NewObjectNode();
 
 
-        for (int i = 0; i < arguments.size(); ++ i) {
+        for (int i = 0; i < argumentNodes.size(); ++ i) {
             newObjectNode.addArgumentNode(cast(argumentOutputs.get(i).expressionNode, argumentCasts.get(i)));
             newObjectNode.addArgumentNode(cast(argumentOutputs.get(i).expressionNode, argumentCasts.get(i)));
         }
         }
 
 
-        newObjectNode.setLocation(location);
+        newObjectNode.setLocation(getLocation());
         newObjectNode.setExpressionType(output.actual);
         newObjectNode.setExpressionType(output.actual);
         newObjectNode.setRead(input.read);
         newObjectNode.setRead(input.read);
         newObjectNode.setConstructor(constructor);
         newObjectNode.setConstructor(constructor);

+ 3 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENull.java

@@ -31,8 +31,8 @@ import org.elasticsearch.painless.symbol.ScriptRoot;
  */
  */
 public class ENull extends AExpression {
 public class ENull extends AExpression {
 
 
-    public ENull(Location location) {
-        super(location);
+    public ENull(int identifier, Location location) {
+        super(identifier, location);
     }
     }
 
 
     @Override
     @Override
@@ -60,7 +60,7 @@ public class ENull extends AExpression {
 
 
         NullNode nullNode = new NullNode();
         NullNode nullNode = new NullNode();
 
 
-        nullNode.setLocation(location);
+        nullNode.setLocation(getLocation());
         nullNode.setExpressionType(output.actual);
         nullNode.setExpressionType(output.actual);
 
 
         output.expressionNode = nullNode;
         output.expressionNode = nullNode;

+ 33 - 25
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENumeric.java

@@ -32,69 +32,77 @@ import java.util.Objects;
  */
  */
 public class ENumeric extends AExpression {
 public class ENumeric extends AExpression {
 
 
-    protected final String value;
-    protected final int radix;
+    private final String numeric;
+    private final int radix;
 
 
-    public ENumeric(Location location, String value, int radix) {
-        super(location);
+    public ENumeric(int identifier, Location location, String numeric, int radix) {
+        super(identifier, location);
 
 
-        this.value = Objects.requireNonNull(value);
+        this.numeric = Objects.requireNonNull(numeric);
         this.radix = radix;
         this.radix = radix;
     }
     }
 
 
+    public String getNumeric() {
+        return numeric;
+    }
+
+    public int getRadix() {
+        return radix;
+    }
+
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
-        return analyze(classNode, scriptRoot, scope, input, false);
+        return analyze(input, false);
     }
     }
 
 
-    Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input, boolean negate) {
+    Output analyze(Input input, boolean negate) {
         if (input.write) {
         if (input.write) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "invalid assignment: cannot assign a value to numeric constant [" + value + "]"));
+                    "invalid assignment: cannot assign a value to numeric constant [" + numeric + "]"));
         }
         }
 
 
         if (input.read == false) {
         if (input.read == false) {
-            throw createError(new IllegalArgumentException("not a statement: numeric constant [" + value + "] not used"));
+            throw createError(new IllegalArgumentException("not a statement: numeric constant [" + numeric + "] not used"));
         }
         }
 
 
         Output output = new Output();
         Output output = new Output();
         Object constant;
         Object constant;
 
 
-        String value = negate ? "-" + this.value : this.value;
+        String numeric = negate ? "-" + this.numeric : this.numeric;
 
 
-        if (value.endsWith("d") || value.endsWith("D")) {
+        if (numeric.endsWith("d") || numeric.endsWith("D")) {
             if (radix != 10) {
             if (radix != 10) {
                 throw createError(new IllegalStateException("Illegal tree structure."));
                 throw createError(new IllegalStateException("Illegal tree structure."));
             }
             }
 
 
             try {
             try {
-                constant = Double.parseDouble(value.substring(0, value.length() - 1));
+                constant = Double.parseDouble(numeric.substring(0, numeric.length() - 1));
                 output.actual = double.class;
                 output.actual = double.class;
             } catch (NumberFormatException exception) {
             } catch (NumberFormatException exception) {
-                throw createError(new IllegalArgumentException("Invalid double constant [" + value + "]."));
+                throw createError(new IllegalArgumentException("Invalid double constant [" + numeric + "]."));
             }
             }
-        } else if (value.endsWith("f") || value.endsWith("F")) {
+        } else if (numeric.endsWith("f") || numeric.endsWith("F")) {
             if (radix != 10) {
             if (radix != 10) {
                 throw createError(new IllegalStateException("Illegal tree structure."));
                 throw createError(new IllegalStateException("Illegal tree structure."));
             }
             }
 
 
             try {
             try {
-                constant = Float.parseFloat(value.substring(0, value.length() - 1));
+                constant = Float.parseFloat(numeric.substring(0, numeric.length() - 1));
                 output.actual = float.class;
                 output.actual = float.class;
             } catch (NumberFormatException exception) {
             } catch (NumberFormatException exception) {
-                throw createError(new IllegalArgumentException("Invalid float constant [" + value + "]."));
+                throw createError(new IllegalArgumentException("Invalid float constant [" + numeric + "]."));
             }
             }
-        } else if (value.endsWith("l") || value.endsWith("L")) {
+        } else if (numeric.endsWith("l") || numeric.endsWith("L")) {
             try {
             try {
-                constant = Long.parseLong(value.substring(0, value.length() - 1), radix);
+                constant = Long.parseLong(numeric.substring(0, numeric.length() - 1), radix);
                 output.actual = long.class;
                 output.actual = long.class;
             } catch (NumberFormatException exception) {
             } catch (NumberFormatException exception) {
-                throw createError(new IllegalArgumentException("Invalid long constant [" + value + "]."));
+                throw createError(new IllegalArgumentException("Invalid long constant [" + numeric + "]."));
             }
             }
         } else {
         } else {
             try {
             try {
                 Class<?> sort = input.expected == null ? int.class : input.expected;
                 Class<?> sort = input.expected == null ? int.class : input.expected;
-                int integer = Integer.parseInt(value, radix);
+                int integer = Integer.parseInt(numeric, radix);
 
 
                 if (sort == byte.class && integer >= Byte.MIN_VALUE && integer <= Byte.MAX_VALUE) {
                 if (sort == byte.class && integer >= Byte.MIN_VALUE && integer <= Byte.MAX_VALUE) {
                     constant = (byte)integer;
                     constant = (byte)integer;
@@ -112,18 +120,18 @@ public class ENumeric extends AExpression {
             } catch (NumberFormatException exception) {
             } catch (NumberFormatException exception) {
                 try {
                 try {
                     // Check if we can parse as a long. If so then hint that the user might prefer that.
                     // Check if we can parse as a long. If so then hint that the user might prefer that.
-                    Long.parseLong(value, radix);
-                    throw createError(new IllegalArgumentException("Invalid int constant [" + value + "]. If you want a long constant "
-                            + "then change it to [" + value + "L]."));
+                    Long.parseLong(numeric, radix);
+                    throw createError(new IllegalArgumentException("Invalid int constant [" + numeric + "]. If you want a long constant "
+                            + "then change it to [" + numeric + "L]."));
                 } catch (NumberFormatException longNoGood) {
                 } catch (NumberFormatException longNoGood) {
                     // Ignored
                     // Ignored
                 }
                 }
-                throw createError(new IllegalArgumentException("Invalid int constant [" + value + "]."));
+                throw createError(new IllegalArgumentException("Invalid int constant [" + numeric + "]."));
             }
             }
         }
         }
 
 
         ConstantNode constantNode = new ConstantNode();
         ConstantNode constantNode = new ConstantNode();
-        constantNode.setLocation(location);
+        constantNode.setLocation(getLocation());
         constantNode.setExpressionType(output.actual);
         constantNode.setExpressionType(output.actual);
         constantNode.setConstant(constant);
         constantNode.setConstant(constant);
 
 

+ 23 - 15
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ERegex.java

@@ -45,14 +45,22 @@ import java.util.regex.PatternSyntaxException;
  */
  */
 public class ERegex extends AExpression {
 public class ERegex extends AExpression {
 
 
-    protected final String pattern;
-    protected final String flags;
+    private final String pattern;
+    private final String flags;
 
 
-    public ERegex(Location location, String pattern, String flags) {
-        super(location);
+    public ERegex(int identifier, Location location, String pattern, String flags) {
+        super(identifier, location);
 
 
         this.pattern = Objects.requireNonNull(pattern);
         this.pattern = Objects.requireNonNull(pattern);
-        this.flags = flags;
+        this.flags = Objects.requireNonNull(flags);
+    }
+
+    public String getPattern() {
+        return pattern;
+    }
+
+    public String getFlags() {
+        return flags;
     }
     }
 
 
     @Override
     @Override
@@ -84,7 +92,7 @@ public class ERegex extends AExpression {
         try {
         try {
             Pattern.compile(pattern, flags);
             Pattern.compile(pattern, flags);
         } catch (PatternSyntaxException e) {
         } catch (PatternSyntaxException e) {
-            throw new Location(location.getSourceName(), location.getOffset() + 1 + e.getIndex()).createError(
+            throw new Location(getLocation().getSourceName(), getLocation().getOffset() + 1 + e.getIndex()).createError(
                     new IllegalArgumentException("Error compiling regex: " + e.getDescription()));
                     new IllegalArgumentException("Error compiling regex: " + e.getDescription()));
         }
         }
 
 
@@ -92,7 +100,7 @@ public class ERegex extends AExpression {
         output.actual = Pattern.class;
         output.actual = Pattern.class;
 
 
         FieldNode fieldNode = new FieldNode();
         FieldNode fieldNode = new FieldNode();
-        fieldNode.setLocation(location);
+        fieldNode.setLocation(getLocation());
         fieldNode.setModifiers(Modifier.FINAL | Modifier.STATIC | Modifier.PRIVATE);
         fieldNode.setModifiers(Modifier.FINAL | Modifier.STATIC | Modifier.PRIVATE);
         fieldNode.setFieldType(Pattern.class);
         fieldNode.setFieldType(Pattern.class);
         fieldNode.setName(name);
         fieldNode.setName(name);
@@ -101,13 +109,13 @@ public class ERegex extends AExpression {
 
 
         try {
         try {
             StatementExpressionNode statementExpressionNode = new StatementExpressionNode();
             StatementExpressionNode statementExpressionNode = new StatementExpressionNode();
-            statementExpressionNode.setLocation(location);
+            statementExpressionNode.setLocation(getLocation());
 
 
             BlockNode blockNode = classNode.getClinitBlockNode();
             BlockNode blockNode = classNode.getClinitBlockNode();
             blockNode.addStatementNode(statementExpressionNode);
             blockNode.addStatementNode(statementExpressionNode);
 
 
             MemberFieldStoreNode memberFieldStoreNode = new MemberFieldStoreNode();
             MemberFieldStoreNode memberFieldStoreNode = new MemberFieldStoreNode();
-            memberFieldStoreNode.setLocation(location);
+            memberFieldStoreNode.setLocation(getLocation());
             memberFieldStoreNode.setExpressionType(void.class);
             memberFieldStoreNode.setExpressionType(void.class);
             memberFieldStoreNode.setFieldType(Pattern.class);
             memberFieldStoreNode.setFieldType(Pattern.class);
             memberFieldStoreNode.setName(name);
             memberFieldStoreNode.setName(name);
@@ -116,19 +124,19 @@ public class ERegex extends AExpression {
             statementExpressionNode.setExpressionNode(memberFieldStoreNode);
             statementExpressionNode.setExpressionNode(memberFieldStoreNode);
 
 
             CallNode callNode = new CallNode();
             CallNode callNode = new CallNode();
-            callNode.setLocation(location);
+            callNode.setLocation(getLocation());
             callNode.setExpressionType(Pattern.class);
             callNode.setExpressionType(Pattern.class);
 
 
             memberFieldStoreNode.setChildNode(callNode);
             memberFieldStoreNode.setChildNode(callNode);
 
 
             StaticNode staticNode = new StaticNode();
             StaticNode staticNode = new StaticNode();
-            staticNode.setLocation(location);
+            staticNode.setLocation(getLocation());
             staticNode.setExpressionType(Pattern.class);
             staticNode.setExpressionType(Pattern.class);
 
 
             callNode.setLeftNode(staticNode);
             callNode.setLeftNode(staticNode);
 
 
             CallSubNode callSubNode = new CallSubNode();
             CallSubNode callSubNode = new CallSubNode();
-            callSubNode.setLocation(location);
+            callSubNode.setLocation(getLocation());
             callSubNode.setExpressionType(Pattern.class);
             callSubNode.setExpressionType(Pattern.class);
             callSubNode.setBox(Pattern.class);
             callSubNode.setBox(Pattern.class);
             callSubNode.setMethod(new PainlessMethod(
             callSubNode.setMethod(new PainlessMethod(
@@ -145,14 +153,14 @@ public class ERegex extends AExpression {
             callNode.setRightNode(callSubNode);
             callNode.setRightNode(callSubNode);
 
 
             ConstantNode constantNode = new ConstantNode();
             ConstantNode constantNode = new ConstantNode();
-            constantNode.setLocation(location);
+            constantNode.setLocation(getLocation());
             constantNode.setExpressionType(String.class);
             constantNode.setExpressionType(String.class);
             constantNode.setConstant(pattern);
             constantNode.setConstant(pattern);
 
 
             callSubNode.addArgumentNode(constantNode);
             callSubNode.addArgumentNode(constantNode);
 
 
             constantNode = new ConstantNode();
             constantNode = new ConstantNode();
-            constantNode.setLocation(location);
+            constantNode.setLocation(getLocation());
             constantNode.setExpressionType(int.class);
             constantNode.setExpressionType(int.class);
             constantNode.setConstant(flags);
             constantNode.setConstant(flags);
 
 
@@ -162,7 +170,7 @@ public class ERegex extends AExpression {
         }
         }
 
 
         MemberFieldLoadNode memberFieldLoadNode = new MemberFieldLoadNode();
         MemberFieldLoadNode memberFieldLoadNode = new MemberFieldLoadNode();
-        memberFieldLoadNode.setLocation(location);
+        memberFieldLoadNode.setLocation(getLocation());
         memberFieldLoadNode.setExpressionType(Pattern.class);
         memberFieldLoadNode.setExpressionType(Pattern.class);
         memberFieldLoadNode.setName(name);
         memberFieldLoadNode.setName(name);
         memberFieldLoadNode.setStatic(true);
         memberFieldLoadNode.setStatic(true);

+ 12 - 8
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EString.java

@@ -32,32 +32,36 @@ import java.util.Objects;
  */
  */
 public class EString extends AExpression {
 public class EString extends AExpression {
 
 
-    protected String constant;
+    private String string;
 
 
-    public EString(Location location, String string) {
-        super(location);
+    public EString(int identifier, Location location, String string) {
+        super(identifier, location);
 
 
-        this.constant = Objects.requireNonNull(string);
+        this.string = Objects.requireNonNull(string);
+    }
+
+    public String getString() {
+        return string;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         if (input.write) {
         if (input.write) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                    "invalid assignment: cannot assign a value to string constant [" + constant + "]"));
+                    "invalid assignment: cannot assign a value to string constant [" + string + "]"));
         }
         }
 
 
         if (input.read == false) {
         if (input.read == false) {
-            throw createError(new IllegalArgumentException("not a statement: string constant [" + constant + "] not used"));
+            throw createError(new IllegalArgumentException("not a statement: string constant [" + string + "] not used"));
         }
         }
 
 
         Output output = new Output();
         Output output = new Output();
         output.actual = String.class;
         output.actual = String.class;
 
 
         ConstantNode constantNode = new ConstantNode();
         ConstantNode constantNode = new ConstantNode();
-        constantNode.setLocation(location);
+        constantNode.setLocation(getLocation());
         constantNode.setExpressionType(output.actual);
         constantNode.setExpressionType(output.actual);
-        constantNode.setConstant(constant);
+        constantNode.setConstant(string);
 
 
         output.expressionNode = constantNode;
         output.expressionNode = constantNode;
 
 

+ 17 - 13
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EVariable.java → modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ESymbol.java

@@ -33,20 +33,24 @@ import java.util.Objects;
 /**
 /**
  * Represents a variable load/store.
  * Represents a variable load/store.
  */
  */
-public class EVariable extends AExpression {
+public class ESymbol extends AExpression {
 
 
-    protected final String name;
+    private final String symbol;
 
 
-    public EVariable(Location location, String name) {
-        super(location);
+    public ESymbol(int identifer, Location location, String symbol) {
+        super(identifer, location);
 
 
-        this.name = Objects.requireNonNull(name);
+        this.symbol = Objects.requireNonNull(symbol);
+    }
+
+    public String getSymbol() {
+        return symbol;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         Output output = new Output();
         Output output = new Output();
-        Class<?> type = scriptRoot.getPainlessLookup().canonicalTypeNameToType(name);
+        Class<?> type = scriptRoot.getPainlessLookup().canonicalTypeNameToType(symbol);
 
 
         if (type != null)  {
         if (type != null)  {
             if (input.write) {
             if (input.write) {
@@ -64,16 +68,16 @@ public class EVariable extends AExpression {
 
 
             StaticNode staticNode = new StaticNode();
             StaticNode staticNode = new StaticNode();
 
 
-            staticNode.setLocation(location);
+            staticNode.setLocation(getLocation());
             staticNode.setExpressionType(output.actual);
             staticNode.setExpressionType(output.actual);
 
 
             output.expressionNode = staticNode;
             output.expressionNode = staticNode;
-        } else if (scope.isVariableDefined(name)) {
+        } else if (scope.isVariableDefined(symbol)) {
             if (input.read == false && input.write == false) {
             if (input.read == false && input.write == false) {
-                throw createError(new IllegalArgumentException("not a statement: variable [" + name + "] not used"));
+                throw createError(new IllegalArgumentException("not a statement: variable [" + symbol + "] not used"));
             }
             }
 
 
-            Variable variable = scope.getVariable(location, name);
+            Variable variable = scope.getVariable(getLocation(), symbol);
 
 
             if (input.write && variable.isFinal()) {
             if (input.write && variable.isFinal()) {
                 throw createError(new IllegalArgumentException("Variable [" + variable.getName() + "] is read-only."));
                 throw createError(new IllegalArgumentException("Variable [" + variable.getName() + "] is read-only."));
@@ -83,13 +87,13 @@ public class EVariable extends AExpression {
 
 
             VariableNode variableNode = new VariableNode();
             VariableNode variableNode = new VariableNode();
 
 
-            variableNode.setLocation(location);
+            variableNode.setLocation(getLocation());
             variableNode.setExpressionType(output.actual);
             variableNode.setExpressionType(output.actual);
-            variableNode.setName(name);
+            variableNode.setName(symbol);
 
 
             output.expressionNode = variableNode;
             output.expressionNode = variableNode;
         } else {
         } else {
-            output.partialCanonicalTypeName = name;
+            output.partialCanonicalTypeName = symbol;
         }
         }
 
 
         return output;
         return output;

+ 30 - 20
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EUnary.java

@@ -37,14 +37,22 @@ import java.util.Objects;
  */
  */
 public class EUnary extends AExpression {
 public class EUnary extends AExpression {
 
 
-    protected final Operation operation;
-    protected final AExpression child;
+    private final AExpression childNode;
+    private final Operation operation;
 
 
-    public EUnary(Location location, Operation operation, AExpression child) {
-        super(location);
+    public EUnary(int identifier, Location location, AExpression childNode, Operation operation) {
+        super(identifier, location);
 
 
+        this.childNode = Objects.requireNonNull(childNode);
         this.operation = Objects.requireNonNull(operation);
         this.operation = Objects.requireNonNull(operation);
-        this.child = Objects.requireNonNull(child);
+    }
+
+    public AExpression getChildNode() {
+        return childNode;
+    }
+
+    public Operation getOperation() {
+        return operation;
     }
     }
 
 
     @Override
     @Override
@@ -67,25 +75,27 @@ public class EUnary extends AExpression {
         Input childInput = new Input();
         Input childInput = new Input();
         Output childOutput;
         Output childOutput;
 
 
-        ENumeric numeric = (ENumeric)child.getChildIf(ENumeric.class);
-        EDecimal decimal = (EDecimal)child.getChildIf(EDecimal.class);
-
-        if ((operation == Operation.SUB || operation == Operation.ADD) && (numeric != null || decimal != null)) {
+        if ((operation == Operation.SUB || operation == Operation.ADD) &&
+                (childNode instanceof ENumeric || childNode instanceof EDecimal)) {
             childInput.expected = input.expected;
             childInput.expected = input.expected;
             childInput.explicit = input.explicit;
             childInput.explicit = input.explicit;
             childInput.internal = input.internal;
             childInput.internal = input.internal;
 
 
-            if (numeric != null) {
+            if (childNode instanceof ENumeric) {
+                ENumeric numeric = (ENumeric)childNode;
+
                 if (operation == Operation.SUB) {
                 if (operation == Operation.SUB) {
-                    childOutput = numeric.analyze(classNode, scriptRoot, scope, childInput, numeric.value.charAt(0) != '-');
+                    childOutput = numeric.analyze(childInput, numeric.getNumeric().charAt(0) != '-');
                 } else {
                 } else {
-                    childOutput = child.analyze(classNode, scriptRoot, scope, childInput);
+                    childOutput = childNode.analyze(classNode, scriptRoot, scope, childInput);
                 }
                 }
-            } else if (decimal != null) {
+            } else if (childNode instanceof EDecimal) {
+                EDecimal decimal = (EDecimal)childNode;
+
                 if (operation == Operation.SUB) {
                 if (operation == Operation.SUB) {
-                    childOutput = decimal.analyze(classNode, scriptRoot, scope, childInput, decimal.value.charAt(0) != '-');
+                    childOutput = decimal.analyze(childInput, decimal.getDecimal().charAt(0) != '-');
                 } else {
                 } else {
-                    childOutput = child.analyze(classNode, scriptRoot, scope, childInput);
+                    childOutput = childNode.analyze(classNode, scriptRoot, scope, childInput);
                 }
                 }
             } else {
             } else {
                 throw createError(new IllegalArgumentException("illegal tree structure"));
                 throw createError(new IllegalArgumentException("illegal tree structure"));
@@ -98,13 +108,13 @@ public class EUnary extends AExpression {
 
 
             if (operation == Operation.NOT) {
             if (operation == Operation.NOT) {
                 childInput.expected = boolean.class;
                 childInput.expected = boolean.class;
-                childOutput = analyze(child, classNode, scriptRoot, scope, childInput);
-                childCast = AnalyzerCaster.getLegalCast(child.location,
+                childOutput = analyze(childNode, classNode, scriptRoot, scope, childInput);
+                childCast = AnalyzerCaster.getLegalCast(childNode.getLocation(),
                         childOutput.actual, childInput.expected, childInput.explicit, childInput.internal);
                         childOutput.actual, childInput.expected, childInput.explicit, childInput.internal);
 
 
                 output.actual = boolean.class;
                 output.actual = boolean.class;
             } else if (operation == Operation.BWNOT || operation == Operation.ADD || operation == Operation.SUB) {
             } else if (operation == Operation.BWNOT || operation == Operation.ADD || operation == Operation.SUB) {
-                childOutput = analyze(child, classNode, scriptRoot, scope, new Input());
+                childOutput = analyze(childNode, classNode, scriptRoot, scope, new Input());
 
 
                 promote = AnalyzerCaster.promoteNumeric(childOutput.actual, operation != Operation.BWNOT);
                 promote = AnalyzerCaster.promoteNumeric(childOutput.actual, operation != Operation.BWNOT);
 
 
@@ -115,7 +125,7 @@ public class EUnary extends AExpression {
                 }
                 }
 
 
                 childInput.expected = promote;
                 childInput.expected = promote;
-                childCast = AnalyzerCaster.getLegalCast(child.location,
+                childCast = AnalyzerCaster.getLegalCast(childNode.getLocation(),
                         childOutput.actual, childInput.expected, childInput.explicit, childInput.internal);
                         childOutput.actual, childInput.expected, childInput.explicit, childInput.internal);
 
 
                 if (promote == def.class && input.expected != null) {
                 if (promote == def.class && input.expected != null) {
@@ -129,7 +139,7 @@ public class EUnary extends AExpression {
 
 
             UnaryMathNode unaryMathNode = new UnaryMathNode();
             UnaryMathNode unaryMathNode = new UnaryMathNode();
             unaryMathNode.setChildNode(cast(childOutput.expressionNode, childCast));
             unaryMathNode.setChildNode(cast(childOutput.expressionNode, childCast));
-            unaryMathNode.setLocation(location);
+            unaryMathNode.setLocation(getLocation());
             unaryMathNode.setExpressionType(output.actual);
             unaryMathNode.setExpressionType(output.actual);
             unaryMathNode.setUnaryType(promote);
             unaryMathNode.setUnaryType(promote);
             unaryMathNode.setOperation(operation);
             unaryMathNode.setOperation(operation);

+ 14 - 9
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBlock.java

@@ -28,33 +28,38 @@ import org.elasticsearch.painless.symbol.ScriptRoot;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
+import java.util.Objects;
 
 
 /**
 /**
  * Represents a set of statements as a branch of control-flow.
  * Represents a set of statements as a branch of control-flow.
  */
  */
 public class SBlock extends AStatement {
 public class SBlock extends AStatement {
 
 
-    protected final List<AStatement> statements;
+    private final List<AStatement> statementNodes;
 
 
-    public SBlock(Location location, List<AStatement> statements) {
-        super(location);
+    public SBlock(int identifier, Location location, List<AStatement> statementNodes) {
+        super(identifier, location);
 
 
-        this.statements = Collections.unmodifiableList(statements);
+        this.statementNodes = Collections.unmodifiableList(Objects.requireNonNull(statementNodes));
+    }
+
+    public List<AStatement> getStatementNodes() {
+        return statementNodes;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         Output output = new Output();
         Output output = new Output();
 
 
-        if (statements == null || statements.isEmpty()) {
+        if (statementNodes.isEmpty()) {
             throw createError(new IllegalArgumentException("A block must contain at least one statement."));
             throw createError(new IllegalArgumentException("A block must contain at least one statement."));
         }
         }
 
 
-        AStatement last = statements.get(statements.size() - 1);
+        AStatement last = statementNodes.get(statementNodes.size() - 1);
 
 
-        List<Output> statementOutputs = new ArrayList<>(statements.size());
+        List<Output> statementOutputs = new ArrayList<>(statementNodes.size());
 
 
-        for (AStatement statement : statements) {
+        for (AStatement statement : statementNodes) {
             // Note that we do not need to check after the last statement because
             // Note that we do not need to check after the last statement because
             // there is no statement that can be unreachable after the last.
             // there is no statement that can be unreachable after the last.
             if (output.allEscape) {
             if (output.allEscape) {
@@ -84,7 +89,7 @@ public class SBlock extends AStatement {
             blockNode.addStatementNode(statementOutput.statementNode);
             blockNode.addStatementNode(statementOutput.statementNode);
         }
         }
 
 
-        blockNode.setLocation(location);
+        blockNode.setLocation(getLocation());
         blockNode.setAllEscape(output.allEscape);
         blockNode.setAllEscape(output.allEscape);
         blockNode.setStatementCount(output.statementCount);
         blockNode.setStatementCount(output.statementCount);
 
 

+ 3 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBreak.java

@@ -30,8 +30,8 @@ import org.elasticsearch.painless.symbol.ScriptRoot;
  */
  */
 public class SBreak extends AStatement {
 public class SBreak extends AStatement {
 
 
-    public SBreak(Location location) {
-        super(location);
+    public SBreak(int identifier, Location location) {
+        super(identifier, location);
     }
     }
 
 
     @Override
     @Override
@@ -48,7 +48,7 @@ public class SBreak extends AStatement {
         output.statementCount = 1;
         output.statementCount = 1;
 
 
         BreakNode breakNode = new BreakNode();
         BreakNode breakNode = new BreakNode();
-        breakNode.setLocation(location);
+        breakNode.setLocation(getLocation());
 
 
         output.statementNode = breakNode;
         output.statementNode = breakNode;
 
 

+ 26 - 15
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java

@@ -35,41 +35,52 @@ import java.util.Objects;
  */
  */
 public class SCatch extends AStatement {
 public class SCatch extends AStatement {
 
 
-    protected final DType baseException;
-    protected final SDeclaration declaration;
-    protected final SBlock block;
+    private final Class<?> baseException;
+    private final SDeclaration declarationNode;
+    private final SBlock blockNode;
 
 
-    public SCatch(Location location, DType baseException, SDeclaration declaration, SBlock block) {
-        super(location);
+    public SCatch(int identifier, Location location, Class<?> baseException, SDeclaration declarationNode, SBlock blockNode) {
+        super(identifier, location);
 
 
         this.baseException = Objects.requireNonNull(baseException);
         this.baseException = Objects.requireNonNull(baseException);
-        this.declaration = Objects.requireNonNull(declaration);
-        this.block = block;
+        this.declarationNode = Objects.requireNonNull(declarationNode);
+        this.blockNode = blockNode;
+    }
+
+    public Class<?> getBaseException() {
+        return baseException;
+    }
+
+    public SDeclaration getDeclarationNode() {
+        return declarationNode;
+    }
+
+    public SBlock getBlockNode() {
+        return blockNode;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         Output output = new Output();
         Output output = new Output();
 
 
-        Output declarationOutput = declaration.analyze(classNode, scriptRoot, scope, new Input());
+        Output declarationOutput = declarationNode.analyze(classNode, scriptRoot, scope, new Input());
 
 
-        Class<?> baseType = baseException.resolveType(scriptRoot.getPainlessLookup()).getType();
-        Class<?> type = scope.getVariable(location, declaration.name).getType();
+        Class<?> type = scope.getVariable(getLocation(), declarationNode.getSymbol()).getType();
 
 
-        if (baseType.isAssignableFrom(type) == false) {
+        if (baseException.isAssignableFrom(type) == false) {
             throw createError(new ClassCastException(
             throw createError(new ClassCastException(
                     "cannot cast from [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] " +
                     "cannot cast from [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] " +
-                    "to [" + PainlessLookupUtility.typeToCanonicalTypeName(baseType) + "]"));
+                    "to [" + PainlessLookupUtility.typeToCanonicalTypeName(baseException) + "]"));
         }
         }
 
 
         Output blockOutput = null;
         Output blockOutput = null;
 
 
-        if (block != null) {
+        if (blockNode != null) {
             Input blockInput = new Input();
             Input blockInput = new Input();
             blockInput.lastSource = input.lastSource;
             blockInput.lastSource = input.lastSource;
             blockInput.inLoop = input.inLoop;
             blockInput.inLoop = input.inLoop;
             blockInput.lastLoop = input.lastLoop;
             blockInput.lastLoop = input.lastLoop;
-            blockOutput = block.analyze(classNode, scriptRoot, scope, blockInput);
+            blockOutput = blockNode.analyze(classNode, scriptRoot, scope, blockInput);
 
 
             output.methodEscape = blockOutput.methodEscape;
             output.methodEscape = blockOutput.methodEscape;
             output.loopEscape = blockOutput.loopEscape;
             output.loopEscape = blockOutput.loopEscape;
@@ -82,7 +93,7 @@ public class SCatch extends AStatement {
         CatchNode catchNode = new CatchNode();
         CatchNode catchNode = new CatchNode();
         catchNode.setDeclarationNode((DeclarationNode)declarationOutput.statementNode);
         catchNode.setDeclarationNode((DeclarationNode)declarationOutput.statementNode);
         catchNode.setBlockNode(blockOutput == null ? null : (BlockNode)blockOutput.statementNode);
         catchNode.setBlockNode(blockOutput == null ? null : (BlockNode)blockOutput.statementNode);
-        catchNode.setLocation(location);
+        catchNode.setLocation(getLocation());
 
 
         output.statementNode = catchNode;
         output.statementNode = catchNode;
 
 

+ 13 - 8
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SClass.java

@@ -23,7 +23,7 @@ import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.ir.ClassNode;
 import org.elasticsearch.painless.ir.ClassNode;
 import org.elasticsearch.painless.symbol.ScriptRoot;
 import org.elasticsearch.painless.symbol.ScriptRoot;
 
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Objects;
 import java.util.Objects;
 
 
@@ -32,15 +32,20 @@ import java.util.Objects;
  */
  */
 public class SClass extends ANode {
 public class SClass extends ANode {
 
 
-    protected final List<SFunction> functions = new ArrayList<>();
+    private final List<SFunction> functionNodes;
 
 
-    public SClass(Location location, List<SFunction> functions) {
-        super(location);
-        this.functions.addAll(Objects.requireNonNull(functions));
+    public SClass(int identifier, Location location, List<SFunction> functionNodes) {
+        super(identifier, location);
+
+        this.functionNodes = Collections.unmodifiableList(Objects.requireNonNull(functionNodes));
+    }
+
+    public List<SFunction> getFunctionNodes() {
+        return functionNodes;
     }
     }
 
 
     public void buildClassScope(ScriptRoot scriptRoot) {
     public void buildClassScope(ScriptRoot scriptRoot) {
-        for (SFunction function : functions) {
+        for (SFunction function : functionNodes) {
             function.buildClassScope(scriptRoot);
             function.buildClassScope(scriptRoot);
         }
         }
     }
     }
@@ -50,11 +55,11 @@ public class SClass extends ANode {
 
 
         ClassNode classNode = new ClassNode();
         ClassNode classNode = new ClassNode();
 
 
-        for (SFunction function : functions) {
+        for (SFunction function : functionNodes) {
             classNode.addFunctionNode(function.writeFunction(classNode, scriptRoot));
             classNode.addFunctionNode(function.writeFunction(classNode, scriptRoot));
         }
         }
 
 
-        classNode.setLocation(location);
+        classNode.setLocation(getLocation());
         classNode.setScriptRoot(scriptRoot);
         classNode.setScriptRoot(scriptRoot);
 
 
         return classNode;
         return classNode;

+ 3 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SContinue.java

@@ -30,8 +30,8 @@ import org.elasticsearch.painless.symbol.ScriptRoot;
  */
  */
 public class SContinue extends AStatement {
 public class SContinue extends AStatement {
 
 
-    public SContinue(Location location) {
-        super(location);
+    public SContinue(int identifier, Location location) {
+        super(identifier, location);
     }
     }
 
 
     @Override
     @Override
@@ -51,7 +51,7 @@ public class SContinue extends AStatement {
         output.statementCount = 1;
         output.statementCount = 1;
 
 
         ContinueNode continueNode = new ContinueNode();
         ContinueNode continueNode = new ContinueNode();
-        continueNode.setLocation(location);
+        continueNode.setLocation(getLocation());
 
 
         output.statementNode = continueNode;
         output.statementNode = continueNode;
 
 

+ 12 - 8
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclBlock.java

@@ -35,25 +35,29 @@ import java.util.List;
  */
  */
 public class SDeclBlock extends AStatement {
 public class SDeclBlock extends AStatement {
 
 
-    protected final List<SDeclaration> declarations;
+    private final List<SDeclaration> declarationNodes;
 
 
-    public SDeclBlock(Location location, List<SDeclaration> declarations) {
-        super(location);
+    public SDeclBlock(int identifier, Location location, List<SDeclaration> declarationNodes) {
+        super(identifier, location);
 
 
-        this.declarations = Collections.unmodifiableList(declarations);
+        this.declarationNodes = Collections.unmodifiableList(declarationNodes);
+    }
+
+    public List<SDeclaration> getDeclarationNodes() {
+        return declarationNodes;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         Output output = new Output();
         Output output = new Output();
 
 
-        List<Output> declarationOutputs = new ArrayList<>(declarations.size());
+        List<Output> declarationOutputs = new ArrayList<>(declarationNodes.size());
 
 
-        for (SDeclaration declaration : declarations) {
+        for (SDeclaration declaration : declarationNodes) {
             declarationOutputs.add(declaration.analyze(classNode, scriptRoot, scope, new Input()));
             declarationOutputs.add(declaration.analyze(classNode, scriptRoot, scope, new Input()));
         }
         }
 
 
-        output.statementCount = declarations.size();
+        output.statementCount = declarationNodes.size();
 
 
         DeclarationBlockNode declarationBlockNode = new DeclarationBlockNode();
         DeclarationBlockNode declarationBlockNode = new DeclarationBlockNode();
 
 
@@ -61,7 +65,7 @@ public class SDeclBlock extends AStatement {
             declarationBlockNode.addDeclarationNode((DeclarationNode)declarationOutput.statementNode);
             declarationBlockNode.addDeclarationNode((DeclarationNode)declarationOutput.statementNode);
         }
         }
 
 
-        declarationBlockNode.setLocation(location);
+        declarationBlockNode.setLocation(getLocation());
 
 
         output.statementNode = declarationBlockNode;
         output.statementNode = declarationBlockNode;
 
 

+ 33 - 17
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java

@@ -34,24 +34,40 @@ import java.util.Objects;
  */
  */
 public class SDeclaration extends AStatement {
 public class SDeclaration extends AStatement {
 
 
-    protected final DType type;
-    protected final String name;
-    protected final boolean requiresDefault;
-    protected final AExpression expression;
+    private final DType type;
+    private final String symbol;
+    private final boolean requiresDefault;
+    private final AExpression valueNode;
 
 
-    public SDeclaration(Location location, DType type, String name, boolean requiresDefault, AExpression expression) {
-        super(location);
+    public SDeclaration(int identifier, Location location, DType type, String symbol, boolean requiresDefault, AExpression valueNode) {
+        super(identifier, location);
 
 
         this.type = Objects.requireNonNull(type);
         this.type = Objects.requireNonNull(type);
-        this.name = Objects.requireNonNull(name);
+        this.symbol = Objects.requireNonNull(symbol);
         this.requiresDefault = requiresDefault;
         this.requiresDefault = requiresDefault;
-        this.expression = expression;
+        this.valueNode = valueNode;
+    }
+
+    public DType getType() {
+        return type;
+    }
+
+    public String getSymbol() {
+        return symbol;
+    }
+
+    public boolean requiresDefault() {
+        return requiresDefault;
+    }
+
+    public AExpression getValueNode() {
+        return valueNode;
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
-        if (scriptRoot.getPainlessLookup().isValidCanonicalClassName(name)) {
-            throw createError(new IllegalArgumentException("invalid declaration: type [" + name + "] cannot be a name"));
+        if (scriptRoot.getPainlessLookup().isValidCanonicalClassName(symbol)) {
+            throw createError(new IllegalArgumentException("invalid declaration: type [" + symbol + "] cannot be a name"));
         }
         }
 
 
         DResolvedType resolvedType = type.resolveType(scriptRoot.getPainlessLookup());
         DResolvedType resolvedType = type.resolveType(scriptRoot.getPainlessLookup());
@@ -59,22 +75,22 @@ public class SDeclaration extends AStatement {
         AExpression.Output expressionOutput = null;
         AExpression.Output expressionOutput = null;
         PainlessCast expressionCast = null;
         PainlessCast expressionCast = null;
 
 
-        if (expression != null) {
+        if (valueNode != null) {
             AExpression.Input expressionInput = new AExpression.Input();
             AExpression.Input expressionInput = new AExpression.Input();
             expressionInput.expected = resolvedType.getType();
             expressionInput.expected = resolvedType.getType();
-            expressionOutput = AExpression.analyze(expression, classNode, scriptRoot, scope, expressionInput);
-            expressionCast = AnalyzerCaster.getLegalCast(expression.location,
+            expressionOutput = AExpression.analyze(valueNode, classNode, scriptRoot, scope, expressionInput);
+            expressionCast = AnalyzerCaster.getLegalCast(valueNode.getLocation(),
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal);
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal);
         }
         }
 
 
-        scope.defineVariable(location, resolvedType.getType(), name, false);
+        scope.defineVariable(getLocation(), resolvedType.getType(), symbol, false);
 
 
         DeclarationNode declarationNode = new DeclarationNode();
         DeclarationNode declarationNode = new DeclarationNode();
-        declarationNode.setExpressionNode(expression == null ? null :
+        declarationNode.setExpressionNode(valueNode == null ? null :
                 AExpression.cast(expressionOutput.expressionNode, expressionCast));
                 AExpression.cast(expressionOutput.expressionNode, expressionCast));
-        declarationNode.setLocation(location);
+        declarationNode.setLocation(getLocation());
         declarationNode.setDeclarationType(resolvedType.getType());
         declarationNode.setDeclarationType(resolvedType.getType());
-        declarationNode.setName(name);
+        declarationNode.setName(symbol);
         declarationNode.setRequiresDefault(requiresDefault);
         declarationNode.setRequiresDefault(requiresDefault);
 
 
         Output output = new Output();
         Output output = new Output();

+ 21 - 13
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDo.java

@@ -35,14 +35,22 @@ import java.util.Objects;
  */
  */
 public class SDo extends AStatement {
 public class SDo extends AStatement {
 
 
-    protected final SBlock block;
-    protected final AExpression condition;
+    private final AExpression conditionNode;
+    private final SBlock blockNode;
 
 
-    public SDo(Location location, SBlock block, AExpression condition) {
-        super(location);
+    public SDo(int identifier, Location location, AExpression conditionNode, SBlock blockNode) {
+        super(identifier, location);
 
 
-        this.condition = Objects.requireNonNull(condition);
-        this.block = block;
+        this.conditionNode = Objects.requireNonNull(conditionNode);
+        this.blockNode = blockNode;
+    }
+
+    public AExpression getConditionNode() {
+        return conditionNode;
+    }
+
+    public SBlock getBlockNode() {
+        return blockNode;
     }
     }
 
 
     @Override
     @Override
@@ -50,14 +58,14 @@ public class SDo extends AStatement {
         Output output = new Output();
         Output output = new Output();
         scope = scope.newLocalScope();
         scope = scope.newLocalScope();
 
 
-        if (block == null) {
+        if (blockNode == null) {
             throw createError(new IllegalArgumentException("Extraneous do while loop."));
             throw createError(new IllegalArgumentException("Extraneous do while loop."));
         }
         }
 
 
         Input blockInput = new Input();
         Input blockInput = new Input();
         blockInput.beginLoop = true;
         blockInput.beginLoop = true;
         blockInput.inLoop = true;
         blockInput.inLoop = true;
-        Output blockOutput = block.analyze(classNode, scriptRoot, scope, blockInput);
+        Output blockOutput = blockNode.analyze(classNode, scriptRoot, scope, blockInput);
 
 
         if (blockOutput.loopEscape && blockOutput.anyContinue == false) {
         if (blockOutput.loopEscape && blockOutput.anyContinue == false) {
             throw createError(new IllegalArgumentException("Extraneous do while loop."));
             throw createError(new IllegalArgumentException("Extraneous do while loop."));
@@ -65,15 +73,15 @@ public class SDo extends AStatement {
 
 
         AExpression.Input conditionInput = new AExpression.Input();
         AExpression.Input conditionInput = new AExpression.Input();
         conditionInput.expected = boolean.class;
         conditionInput.expected = boolean.class;
-        AExpression.Output conditionOutput = AExpression.analyze(condition, classNode, scriptRoot, scope, conditionInput);
-        PainlessCast conditionCast = AnalyzerCaster.getLegalCast(condition.location,
+        AExpression.Output conditionOutput = AExpression.analyze(conditionNode, classNode, scriptRoot, scope, conditionInput);
+        PainlessCast conditionCast = AnalyzerCaster.getLegalCast(conditionNode.getLocation(),
                 conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
                 conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
 
 
 
 
         boolean continuous = false;
         boolean continuous = false;
 
 
-        if (condition.getChildIf(EBoolean.class) != null) {
-            continuous = ((EBoolean)condition).constant;
+        if (conditionNode instanceof EBoolean) {
+            continuous = ((EBoolean)conditionNode).getBool();
 
 
             if (!continuous) {
             if (!continuous) {
                 throw createError(new IllegalArgumentException("Extraneous do while loop."));
                 throw createError(new IllegalArgumentException("Extraneous do while loop."));
@@ -90,7 +98,7 @@ public class SDo extends AStatement {
         DoWhileLoopNode doWhileLoopNode = new DoWhileLoopNode();
         DoWhileLoopNode doWhileLoopNode = new DoWhileLoopNode();
         doWhileLoopNode.setConditionNode(AExpression.cast(conditionOutput.expressionNode, conditionCast));
         doWhileLoopNode.setConditionNode(AExpression.cast(conditionOutput.expressionNode, conditionCast));
         doWhileLoopNode.setBlockNode((BlockNode)blockOutput.statementNode);
         doWhileLoopNode.setBlockNode((BlockNode)blockOutput.statementNode);
-        doWhileLoopNode.setLocation(location);
+        doWhileLoopNode.setLocation(getLocation());
         doWhileLoopNode.setContinuous(continuous);
         doWhileLoopNode.setContinuous(continuous);
 
 
         output.statementNode = doWhileLoopNode;
         output.statementNode = doWhileLoopNode;

+ 42 - 26
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java

@@ -45,18 +45,34 @@ import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCano
  */
  */
 public class SEach extends AStatement {
 public class SEach extends AStatement {
 
 
-    protected final String type;
-    protected final String name;
-    protected final AExpression expression;
-    protected final SBlock block;
-
-    public SEach(Location location, String type, String name, AExpression expression, SBlock block) {
-        super(location);
-
-        this.type = Objects.requireNonNull(type);
-        this.name = Objects.requireNonNull(name);
-        this.expression = Objects.requireNonNull(expression);
-        this.block = block;
+    private final String canonicalTypeName;
+    private final String symbol;
+    private final AExpression iterableNode;
+    private final SBlock blockNode;
+
+    public SEach(int identifier, Location location, String canonicalTypeName, String symbol, AExpression iterableNode, SBlock blockNode) {
+        super(identifier, location);
+
+        this.canonicalTypeName = Objects.requireNonNull(canonicalTypeName);
+        this.symbol = Objects.requireNonNull(symbol);
+        this.iterableNode = Objects.requireNonNull(iterableNode);
+        this.blockNode = blockNode;
+    }
+
+    public String getCanonicalTypeName() {
+        return canonicalTypeName;
+    }
+
+    public String getSymbol() {
+        return symbol;
+    }
+
+    public AExpression getIterableNode() {
+        return iterableNode;
+    }
+
+    public SBlock getBlockNode() {
+        return blockNode;
     }
     }
 
 
     @Override
     @Override
@@ -64,25 +80,25 @@ public class SEach extends AStatement {
         Output output = new Output();
         Output output = new Output();
 
 
         AExpression.Input expressionInput = new AExpression.Input();
         AExpression.Input expressionInput = new AExpression.Input();
-        AExpression.Output expressionOutput = AExpression.analyze(expression, classNode, scriptRoot, scope, expressionInput);
+        AExpression.Output expressionOutput = AExpression.analyze(iterableNode, classNode, scriptRoot, scope, expressionInput);
 
 
-        Class<?> clazz = scriptRoot.getPainlessLookup().canonicalTypeNameToType(this.type);
+        Class<?> clazz = scriptRoot.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
 
 
         if (clazz == null) {
         if (clazz == null) {
-            throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
+            throw createError(new IllegalArgumentException("Not a type [" + canonicalTypeName + "]."));
         }
         }
 
 
         scope = scope.newLocalScope();
         scope = scope.newLocalScope();
-        Variable variable = scope.defineVariable(location, clazz, name, true);
+        Variable variable = scope.defineVariable(getLocation(), clazz, symbol, true);
 
 
-        if (block == null) {
+        if (blockNode == null) {
             throw createError(new IllegalArgumentException("Extraneous for each loop."));
             throw createError(new IllegalArgumentException("Extraneous for each loop."));
         }
         }
 
 
         Input blockInput = new Input();
         Input blockInput = new Input();
         blockInput.beginLoop = true;
         blockInput.beginLoop = true;
         blockInput.inLoop = true;
         blockInput.inLoop = true;
-        Output blockOutput = block.analyze(classNode, scriptRoot, scope, blockInput);
+        Output blockOutput = blockNode.analyze(classNode, scriptRoot, scope, blockInput);
         blockOutput.statementCount = Math.max(1, blockOutput.statementCount);
         blockOutput.statementCount = Math.max(1, blockOutput.statementCount);
 
 
         if (blockOutput.loopEscape && blockOutput.anyContinue == false) {
         if (blockOutput.loopEscape && blockOutput.anyContinue == false) {
@@ -92,15 +108,15 @@ public class SEach extends AStatement {
         ConditionNode conditionNode;
         ConditionNode conditionNode;
 
 
         if (expressionOutput.actual.isArray()) {
         if (expressionOutput.actual.isArray()) {
-            Variable array = scope.defineVariable(location, expressionOutput.actual, "#array" + location.getOffset(), true);
-            Variable index = scope.defineVariable(location, int.class, "#index" + location.getOffset(), true);
+            Variable array = scope.defineVariable(getLocation(), expressionOutput.actual, "#array" + getLocation().getOffset(), true);
+            Variable index = scope.defineVariable(getLocation(), int.class, "#index" + getLocation().getOffset(), true);
             Class<?> indexed = expressionOutput.actual.getComponentType();
             Class<?> indexed = expressionOutput.actual.getComponentType();
-            PainlessCast cast = AnalyzerCaster.getLegalCast(location, indexed, variable.getType(), true, true);
+            PainlessCast cast = AnalyzerCaster.getLegalCast(getLocation(), indexed, variable.getType(), true, true);
 
 
             ForEachSubArrayNode forEachSubArrayNode = new ForEachSubArrayNode();
             ForEachSubArrayNode forEachSubArrayNode = new ForEachSubArrayNode();
             forEachSubArrayNode.setConditionNode(expressionOutput.expressionNode);
             forEachSubArrayNode.setConditionNode(expressionOutput.expressionNode);
             forEachSubArrayNode.setBlockNode((BlockNode)blockOutput.statementNode);
             forEachSubArrayNode.setBlockNode((BlockNode)blockOutput.statementNode);
-            forEachSubArrayNode.setLocation(location);
+            forEachSubArrayNode.setLocation(getLocation());
             forEachSubArrayNode.setVariableType(variable.getType());
             forEachSubArrayNode.setVariableType(variable.getType());
             forEachSubArrayNode.setVariableName(variable.getName());
             forEachSubArrayNode.setVariableName(variable.getName());
             forEachSubArrayNode.setCast(cast);
             forEachSubArrayNode.setCast(cast);
@@ -114,7 +130,7 @@ public class SEach extends AStatement {
         } else if (expressionOutput.actual == def.class || Iterable.class.isAssignableFrom(expressionOutput.actual)) {
         } else if (expressionOutput.actual == def.class || Iterable.class.isAssignableFrom(expressionOutput.actual)) {
             // We must store the iterator as a variable for securing a slot on the stack, and
             // 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.
             // also add the location offset to make the name unique in case of nested for each loops.
-            Variable iterator = scope.defineVariable(location, Iterator.class, "#itr" + location.getOffset(), true);
+            Variable iterator = scope.defineVariable(getLocation(), Iterator.class, "#itr" + getLocation().getOffset(), true);
 
 
             PainlessMethod method;
             PainlessMethod method;
 
 
@@ -129,12 +145,12 @@ public class SEach extends AStatement {
                 }
                 }
             }
             }
 
 
-            PainlessCast cast = AnalyzerCaster.getLegalCast(location, def.class, variable.getType(), true, true);
+            PainlessCast cast = AnalyzerCaster.getLegalCast(getLocation(), def.class, variable.getType(), true, true);
 
 
             ForEachSubIterableNode forEachSubIterableNode = new ForEachSubIterableNode();
             ForEachSubIterableNode forEachSubIterableNode = new ForEachSubIterableNode();
             forEachSubIterableNode.setConditionNode(expressionOutput.expressionNode);
             forEachSubIterableNode.setConditionNode(expressionOutput.expressionNode);
             forEachSubIterableNode.setBlockNode((BlockNode)blockOutput.statementNode);
             forEachSubIterableNode.setBlockNode((BlockNode)blockOutput.statementNode);
-            forEachSubIterableNode.setLocation(location);
+            forEachSubIterableNode.setLocation(getLocation());
             forEachSubIterableNode.setVariableType(variable.getType());
             forEachSubIterableNode.setVariableType(variable.getType());
             forEachSubIterableNode.setVariableName(variable.getName());
             forEachSubIterableNode.setVariableName(variable.getName());
             forEachSubIterableNode.setCast(cast);
             forEachSubIterableNode.setCast(cast);
@@ -152,7 +168,7 @@ public class SEach extends AStatement {
 
 
         ForEachLoopNode forEachLoopNode = new ForEachLoopNode();
         ForEachLoopNode forEachLoopNode = new ForEachLoopNode();
         forEachLoopNode.setConditionNode(conditionNode);
         forEachLoopNode.setConditionNode(conditionNode);
-        forEachLoopNode.setLocation(location);
+        forEachLoopNode.setLocation(getLocation());
 
 
         output.statementNode = forEachLoopNode;
         output.statementNode = forEachLoopNode;
 
 

+ 12 - 8
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SExpression.java

@@ -36,12 +36,16 @@ import java.util.Objects;
  */
  */
 public class SExpression extends AStatement {
 public class SExpression extends AStatement {
 
 
-    protected final AExpression expression;
+    private final AExpression expressionNode;
 
 
-    public SExpression(Location location, AExpression expression) {
-        super(location);
+    public SExpression(int identifier, Location location, AExpression expressionNode) {
+        super(identifier, location);
 
 
-        this.expression = Objects.requireNonNull(expression);
+        this.expressionNode = Objects.requireNonNull(expressionNode);
+    }
+
+    public AExpression getExpressionNode() {
+        return expressionNode;
     }
     }
 
 
     @Override
     @Override
@@ -51,13 +55,13 @@ public class SExpression extends AStatement {
 
 
         AExpression.Input expressionInput = new AExpression.Input();
         AExpression.Input expressionInput = new AExpression.Input();
         expressionInput.read = input.lastSource && !isVoid;
         expressionInput.read = input.lastSource && !isVoid;
-        AExpression.Output expressionOutput = AExpression.analyze(expression, classNode, scriptRoot, scope, expressionInput);
+        AExpression.Output expressionOutput = AExpression.analyze(expressionNode, classNode, scriptRoot, scope, expressionInput);
 
 
         boolean rtn = input.lastSource && isVoid == false && expressionOutput.actual != void.class;
         boolean rtn = input.lastSource && isVoid == false && expressionOutput.actual != void.class;
 
 
         expressionInput.expected = rtn ? rtnType : expressionOutput.actual;
         expressionInput.expected = rtn ? rtnType : expressionOutput.actual;
         expressionInput.internal = rtn;
         expressionInput.internal = rtn;
-        PainlessCast expressionCast = AnalyzerCaster.getLegalCast(expression.location,
+        PainlessCast expressionCast = AnalyzerCaster.getLegalCast(expressionNode.getLocation(),
                 expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal);
                 expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal);
 
 
         Output output = new Output();
         Output output = new Output();
@@ -71,13 +75,13 @@ public class SExpression extends AStatement {
         if (output.methodEscape) {
         if (output.methodEscape) {
             ReturnNode returnNode = new ReturnNode();
             ReturnNode returnNode = new ReturnNode();
             returnNode.setExpressionNode(expressionNode);
             returnNode.setExpressionNode(expressionNode);
-            returnNode.setLocation(location);
+            returnNode.setLocation(getLocation());
 
 
             output.statementNode = returnNode;
             output.statementNode = returnNode;
         } else {
         } else {
             StatementExpressionNode statementExpressionNode = new StatementExpressionNode();
             StatementExpressionNode statementExpressionNode = new StatementExpressionNode();
             statementExpressionNode.setExpressionNode(expressionNode);
             statementExpressionNode.setExpressionNode(expressionNode);
-            statementExpressionNode.setLocation(location);
+            statementExpressionNode.setLocation(getLocation());
 
 
             output.statementNode = statementExpressionNode;
             output.statementNode = statementExpressionNode;
         }
         }

+ 47 - 29
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFor.java

@@ -33,18 +33,36 @@ import org.elasticsearch.painless.symbol.ScriptRoot;
  */
  */
 public class SFor extends AStatement {
 public class SFor extends AStatement {
 
 
-    protected final ANode initializer;
-    protected final AExpression condition;
-    protected final AExpression afterthought;
-    protected final SBlock block;
-
-    public SFor(Location location, ANode initializer, AExpression condition, AExpression afterthought, SBlock block) {
-        super(location);
-
-        this.initializer = initializer;
-        this.condition = condition;
-        this.afterthought = afterthought;
-        this.block = block;
+    private final ANode initializerNode;
+    private final AExpression conditionNode;
+    private final AExpression afterthoughtNode;
+    private final SBlock blockNode;
+
+    public SFor(int identifier, Location location,
+            ANode initializerNode, AExpression conditionNode, AExpression afterthoughtNode, SBlock blockNode) {
+
+        super(identifier, location);
+
+        this.initializerNode = initializerNode;
+        this.conditionNode = conditionNode;
+        this.afterthoughtNode = afterthoughtNode;
+        this.blockNode = blockNode;
+    }
+
+    public ANode getInitializerNode() {
+        return initializerNode;
+    }
+
+    public AExpression getConditionNode() {
+        return conditionNode;
+    }
+
+    public AExpression getAfterthoughtNode() {
+        return afterthoughtNode;
+    }
+
+    public SBlock getBlockNode() {
+        return blockNode;
     }
     }
 
 
     @Override
     @Override
@@ -54,11 +72,11 @@ public class SFor extends AStatement {
         Output initializerStatementOutput = null;
         Output initializerStatementOutput = null;
         AExpression.Output initializerExpressionOutput = null;
         AExpression.Output initializerExpressionOutput = null;
 
 
-        if (initializer != null) {
-            if (initializer instanceof SDeclBlock) {
-                initializerStatementOutput = ((SDeclBlock)initializer).analyze(classNode, scriptRoot, scope, new Input());
-            } else if (initializer instanceof AExpression) {
-                AExpression initializer = (AExpression)this.initializer;
+        if (initializerNode != null) {
+            if (initializerNode instanceof SDeclBlock) {
+                initializerStatementOutput = ((SDeclBlock)initializerNode).analyze(classNode, scriptRoot, scope, new Input());
+            } else if (initializerNode instanceof AExpression) {
+                AExpression initializer = (AExpression)this.initializerNode;
 
 
                 AExpression.Input initializerInput = new AExpression.Input();
                 AExpression.Input initializerInput = new AExpression.Input();
                 initializerInput.read = false;
                 initializerInput.read = false;
@@ -73,21 +91,21 @@ public class SFor extends AStatement {
         AExpression.Output conditionOutput = null;
         AExpression.Output conditionOutput = null;
         PainlessCast conditionCast = null;
         PainlessCast conditionCast = null;
 
 
-        if (condition != null) {
+        if (conditionNode != null) {
             AExpression.Input conditionInput = new AExpression.Input();
             AExpression.Input conditionInput = new AExpression.Input();
             conditionInput.expected = boolean.class;
             conditionInput.expected = boolean.class;
-            conditionOutput = AExpression.analyze(condition, classNode, scriptRoot, scope, conditionInput);
-            conditionCast = AnalyzerCaster.getLegalCast(condition.location,
+            conditionOutput = AExpression.analyze(conditionNode, classNode, scriptRoot, scope, conditionInput);
+            conditionCast = AnalyzerCaster.getLegalCast(conditionNode.getLocation(),
                     conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
                     conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
 
 
-            if (condition.getChildIf(EBoolean.class) != null) {
-                continuous = ((EBoolean)condition).constant;
+            if (conditionNode instanceof EBoolean) {
+                continuous = ((EBoolean)conditionNode).getBool();
 
 
                 if (!continuous) {
                 if (!continuous) {
                     throw createError(new IllegalArgumentException("Extraneous for loop."));
                     throw createError(new IllegalArgumentException("Extraneous for loop."));
                 }
                 }
 
 
-                if (block == null) {
+                if (blockNode == null) {
                     throw createError(new IllegalArgumentException("For loop has no escape."));
                     throw createError(new IllegalArgumentException("For loop has no escape."));
                 }
                 }
             }
             }
@@ -97,21 +115,21 @@ public class SFor extends AStatement {
 
 
         AExpression.Output afterthoughtOutput = null;
         AExpression.Output afterthoughtOutput = null;
 
 
-        if (afterthought != null) {
+        if (afterthoughtNode != null) {
             AExpression.Input afterthoughtInput = new AExpression.Input();
             AExpression.Input afterthoughtInput = new AExpression.Input();
             afterthoughtInput.read = false;
             afterthoughtInput.read = false;
-            afterthoughtOutput = AExpression.analyze(afterthought, classNode, scriptRoot, scope, afterthoughtInput);
+            afterthoughtOutput = AExpression.analyze(afterthoughtNode, classNode, scriptRoot, scope, afterthoughtInput);
         }
         }
 
 
         Output output = new Output();
         Output output = new Output();
         Output blockOutput = null;
         Output blockOutput = null;
 
 
-        if (block != null) {
+        if (blockNode != null) {
             Input blockInput = new Input();
             Input blockInput = new Input();
             blockInput.beginLoop = true;
             blockInput.beginLoop = true;
             blockInput.inLoop = true;
             blockInput.inLoop = true;
 
 
-            blockOutput = block.analyze(classNode, scriptRoot, scope, blockInput);
+            blockOutput = blockNode.analyze(classNode, scriptRoot, scope, blockInput);
 
 
             if (blockOutput.loopEscape && blockOutput.anyContinue == false) {
             if (blockOutput.loopEscape && blockOutput.anyContinue == false) {
                 throw createError(new IllegalArgumentException("Extraneous for loop."));
                 throw createError(new IllegalArgumentException("Extraneous for loop."));
@@ -128,13 +146,13 @@ public class SFor extends AStatement {
         output.statementCount = 1;
         output.statementCount = 1;
 
 
         ForLoopNode forLoopNode = new ForLoopNode();
         ForLoopNode forLoopNode = new ForLoopNode();
-        forLoopNode.setInitialzerNode(initializer == null ? null : initializer instanceof AExpression ?
+        forLoopNode.setInitialzerNode(initializerNode == null ? null : initializerNode instanceof AExpression ?
                 initializerExpressionOutput.expressionNode : initializerStatementOutput.statementNode);
                 initializerExpressionOutput.expressionNode : initializerStatementOutput.statementNode);
         forLoopNode.setConditionNode(conditionOutput == null ?
         forLoopNode.setConditionNode(conditionOutput == null ?
                 null : AExpression.cast(conditionOutput.expressionNode, conditionCast));
                 null : AExpression.cast(conditionOutput.expressionNode, conditionCast));
         forLoopNode.setAfterthoughtNode(afterthoughtOutput == null ? null : afterthoughtOutput.expressionNode);
         forLoopNode.setAfterthoughtNode(afterthoughtOutput == null ? null : afterthoughtOutput.expressionNode);
         forLoopNode.setBlockNode(blockOutput == null ? null : (BlockNode)blockOutput.statementNode);
         forLoopNode.setBlockNode(blockOutput == null ? null : (BlockNode)blockOutput.statementNode);
-        forLoopNode.setLocation(location);
+        forLoopNode.setLocation(getLocation());
         forLoopNode.setContinuous(continuous);
         forLoopNode.setContinuous(continuous);
 
 
         output.statementNode = forLoopNode;
         output.statementNode = forLoopNode;

+ 87 - 49
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java

@@ -47,63 +47,100 @@ import static org.elasticsearch.painless.Scope.newFunctionScope;
  */
  */
 public class SFunction extends ANode {
 public class SFunction extends ANode {
 
 
-    protected final String rtnTypeStr;
-    protected final String name;
-    protected final List<String> paramTypeStrs;
-    protected final List<String> paramNameStrs;
-    protected final SBlock block;
-    protected final boolean isInternal;
-    protected final boolean isStatic;
-    protected final boolean synthetic;
+    private final String returnCanonicalTypeName;
+    private final String functionName;
+    private final List<String> canonicalTypeNameParameters;
+    private final List<String> parameterNames;
+    private final SBlock blockNode;
+    private final boolean isInternal;
+    private final boolean isStatic;
+    private final boolean isSynthetic;
+    private final boolean isAutoReturnEnabled;
+
+    public SFunction(int identifier, Location location,
+            String returnCanonicalTypeName, String name, List<String> canonicalTypeNameParameters, List<String> parameterNames,
+            SBlock blockNode,
+            boolean isInternal, boolean isStatic, boolean isSynthetic, boolean isAutoReturnEnabled) {
+
+        super(identifier, location);
+
+        this.returnCanonicalTypeName = Objects.requireNonNull(returnCanonicalTypeName);
+        this.functionName = Objects.requireNonNull(name);
+        this.canonicalTypeNameParameters = Collections.unmodifiableList(Objects.requireNonNull(canonicalTypeNameParameters));
+        this.parameterNames = Collections.unmodifiableList(Objects.requireNonNull(parameterNames));
+        this.blockNode = Objects.requireNonNull(blockNode);
+        this.isInternal = isInternal;
+        this.isSynthetic = isSynthetic;
+        this.isStatic = isStatic;
+        this.isAutoReturnEnabled = isAutoReturnEnabled;
+    }
+
+    public String getReturnCanonicalTypeName() {
+        return returnCanonicalTypeName;
+    }
+
+    public String getFunctionName() {
+        return functionName;
+    }
+
+    public List<String> getCanonicalTypeNameParameters() {
+        return canonicalTypeNameParameters;
+    }
+
+    public List<String> getParameterNames() {
+        return parameterNames;
+    }
+
+    public SBlock getBlockNode() {
+        return blockNode;
+    }
+
+    public boolean isInternal() {
+        return isInternal;
+    }
+
+    public boolean isStatic() {
+        return isStatic;
+    }
+
+    public boolean isSynthetic() {
+        return isSynthetic;
+    }
 
 
     /**
     /**
      * If set to {@code true} default return values are inserted if
      * If set to {@code true} default return values are inserted if
      * not all paths return a value.
      * not all paths return a value.
      */
      */
-    protected final boolean isAutoReturnEnabled;
-
-    public SFunction(Location location, String rtnType, String name,
-            List<String> paramTypes, List<String> paramNames,
-            SBlock block, boolean isInternal, boolean isStatic, boolean synthetic, boolean isAutoReturnEnabled) {
-        super(location);
-
-        this.rtnTypeStr = Objects.requireNonNull(rtnType);
-        this.name = Objects.requireNonNull(name);
-        this.paramTypeStrs = Collections.unmodifiableList(paramTypes);
-        this.paramNameStrs = Collections.unmodifiableList(paramNames);
-        this.block = Objects.requireNonNull(block);
-        this.isInternal = isInternal;
-        this.synthetic = synthetic;
-        this.isStatic = isStatic;
-        this.isAutoReturnEnabled = isAutoReturnEnabled;
+    public boolean isAutoReturnEnabled() {
+        return isAutoReturnEnabled;
     }
     }
 
 
     void buildClassScope(ScriptRoot scriptRoot) {
     void buildClassScope(ScriptRoot scriptRoot) {
-        if (paramTypeStrs.size() != paramNameStrs.size()) {
+        if (canonicalTypeNameParameters.size() != parameterNames.size()) {
             throw createError(new IllegalStateException(
             throw createError(new IllegalStateException(
-                "parameter types size [" + paramTypeStrs.size() + "] is not equal to " +
-                "parameter names size [" + paramNameStrs.size() + "]"));
+                "parameter types size [" + canonicalTypeNameParameters.size() + "] is not equal to " +
+                "parameter names size [" + parameterNames.size() + "]"));
         }
         }
 
 
         PainlessLookup painlessLookup = scriptRoot.getPainlessLookup();
         PainlessLookup painlessLookup = scriptRoot.getPainlessLookup();
         FunctionTable functionTable = scriptRoot.getFunctionTable();
         FunctionTable functionTable = scriptRoot.getFunctionTable();
 
 
-        String functionKey = FunctionTable.buildLocalFunctionKey(name, paramTypeStrs.size());
+        String functionKey = FunctionTable.buildLocalFunctionKey(functionName, canonicalTypeNameParameters.size());
 
 
         if (functionTable.getFunction(functionKey) != null) {
         if (functionTable.getFunction(functionKey) != null) {
             throw createError(new IllegalArgumentException("illegal duplicate functions [" + functionKey + "]."));
             throw createError(new IllegalArgumentException("illegal duplicate functions [" + functionKey + "]."));
         }
         }
 
 
-        Class<?> returnType = painlessLookup.canonicalTypeNameToType(rtnTypeStr);
+        Class<?> returnType = painlessLookup.canonicalTypeNameToType(returnCanonicalTypeName);
 
 
         if (returnType == null) {
         if (returnType == null) {
             throw createError(new IllegalArgumentException(
             throw createError(new IllegalArgumentException(
-                "return type [" + rtnTypeStr + "] not found for function [" + functionKey + "]"));
+                "return type [" + returnCanonicalTypeName + "] not found for function [" + functionKey + "]"));
         }
         }
 
 
         List<Class<?>> typeParameters = new ArrayList<>();
         List<Class<?>> typeParameters = new ArrayList<>();
 
 
-        for (String typeParameter : paramTypeStrs) {
+        for (String typeParameter : canonicalTypeNameParameters) {
             Class<?> paramType = painlessLookup.canonicalTypeNameToType(typeParameter);
             Class<?> paramType = painlessLookup.canonicalTypeNameToType(typeParameter);
 
 
             if (paramType == null) {
             if (paramType == null) {
@@ -114,40 +151,41 @@ public class SFunction extends ANode {
             typeParameters.add(paramType);
             typeParameters.add(paramType);
         }
         }
 
 
-        functionTable.addFunction(name, returnType, typeParameters, isInternal, isStatic);
+        functionTable.addFunction(functionName, returnType, typeParameters, isInternal, isStatic);
     }
     }
 
 
     FunctionNode writeFunction(ClassNode classNode, ScriptRoot scriptRoot) {
     FunctionNode writeFunction(ClassNode classNode, ScriptRoot scriptRoot) {
-        FunctionTable.LocalFunction localFunction = scriptRoot.getFunctionTable().getFunction(name, paramTypeStrs.size());
+        FunctionTable.LocalFunction localFunction =
+                scriptRoot.getFunctionTable().getFunction(functionName, canonicalTypeNameParameters.size());
         Class<?> returnType = localFunction.getReturnType();
         Class<?> returnType = localFunction.getReturnType();
         List<Class<?>> typeParameters = localFunction.getTypeParameters();
         List<Class<?>> typeParameters = localFunction.getTypeParameters();
         FunctionScope functionScope = newFunctionScope(localFunction.getReturnType());
         FunctionScope functionScope = newFunctionScope(localFunction.getReturnType());
 
 
         for (int index = 0; index < localFunction.getTypeParameters().size(); ++index) {
         for (int index = 0; index < localFunction.getTypeParameters().size(); ++index) {
             Class<?> typeParameter = localFunction.getTypeParameters().get(index);
             Class<?> typeParameter = localFunction.getTypeParameters().get(index);
-            String parameterName = paramNameStrs.get(index);
-            functionScope.defineVariable(location, typeParameter, parameterName, false);
+            String parameterName = parameterNames.get(index);
+            functionScope.defineVariable(getLocation(), typeParameter, parameterName, false);
         }
         }
 
 
         int maxLoopCounter = scriptRoot.getCompilerSettings().getMaxLoopCounter();
         int maxLoopCounter = scriptRoot.getCompilerSettings().getMaxLoopCounter();
 
 
-        if (block.statements.isEmpty()) {
-            throw createError(new IllegalArgumentException("Cannot generate an empty function [" + name + "]."));
+        if (blockNode.getStatementNodes().isEmpty()) {
+            throw createError(new IllegalArgumentException("Cannot generate an empty function [" + functionName + "]."));
         }
         }
 
 
         Input blockInput = new Input();
         Input blockInput = new Input();
         blockInput.lastSource = true;
         blockInput.lastSource = true;
-        Output blockOutput = block.analyze(classNode, scriptRoot, functionScope.newLocalScope(), blockInput);
+        Output blockOutput = blockNode.analyze(classNode, scriptRoot, functionScope.newLocalScope(), blockInput);
         boolean methodEscape = blockOutput.methodEscape;
         boolean methodEscape = blockOutput.methodEscape;
 
 
         if (methodEscape == false && isAutoReturnEnabled == false && returnType != void.class) {
         if (methodEscape == false && isAutoReturnEnabled == false && returnType != void.class) {
             throw createError(new IllegalArgumentException("not all paths provide a return value " +
             throw createError(new IllegalArgumentException("not all paths provide a return value " +
-                    "for function [" + name + "] with [" + typeParameters.size() + "] parameters"));
+                    "for function [" + functionName + "] with [" + typeParameters.size() + "] parameters"));
         }
         }
 
 
         // TODO: do not specialize for execute
         // TODO: do not specialize for execute
         // TODO: https://github.com/elastic/elasticsearch/issues/51841
         // TODO: https://github.com/elastic/elasticsearch/issues/51841
-        if ("execute".equals(name)) {
+        if ("execute".equals(functionName)) {
             scriptRoot.setUsedVariables(functionScope.getUsedVariables());
             scriptRoot.setUsedVariables(functionScope.getUsedVariables());
         }
         }
         // TODO: end
         // TODO: end
@@ -162,7 +200,7 @@ public class SFunction extends ANode {
             } else if (isAutoReturnEnabled) {
             } else if (isAutoReturnEnabled) {
                 if (returnType.isPrimitive()) {
                 if (returnType.isPrimitive()) {
                     ConstantNode constantNode = new ConstantNode();
                     ConstantNode constantNode = new ConstantNode();
-                    constantNode.setLocation(location);
+                    constantNode.setLocation(getLocation());
                     constantNode.setExpressionType(returnType);
                     constantNode.setExpressionType(returnType);
 
 
                     if (returnType == boolean.class) {
                     if (returnType == boolean.class) {
@@ -181,22 +219,22 @@ public class SFunction extends ANode {
                     } else {
                     } else {
                         throw createError(new IllegalStateException("unexpected automatic return type " +
                         throw createError(new IllegalStateException("unexpected automatic return type " +
                                 "[" + PainlessLookupUtility.typeToCanonicalTypeName(returnType) + "] " +
                                 "[" + PainlessLookupUtility.typeToCanonicalTypeName(returnType) + "] " +
-                                "for function [" + name + "] with [" + typeParameters.size() + "] parameters"));
+                                "for function [" + functionName + "] with [" + typeParameters.size() + "] parameters"));
                     }
                     }
 
 
                     expressionNode = constantNode;
                     expressionNode = constantNode;
                 } else {
                 } else {
                     expressionNode = new NullNode();
                     expressionNode = new NullNode();
-                    expressionNode.setLocation(location);
+                    expressionNode.setLocation(getLocation());
                     expressionNode.setExpressionType(returnType);
                     expressionNode.setExpressionType(returnType);
                 }
                 }
             } else {
             } else {
                 throw createError(new IllegalStateException("not all paths provide a return value " +
                 throw createError(new IllegalStateException("not all paths provide a return value " +
-                        "for function [" + name + "] with [" + typeParameters.size() + "] parameters"));
+                        "for function [" + functionName + "] with [" + typeParameters.size() + "] parameters"));
             }
             }
 
 
             ReturnNode returnNode = new ReturnNode();
             ReturnNode returnNode = new ReturnNode();
-            returnNode.setLocation(location);
+            returnNode.setLocation(getLocation());
             returnNode.setExpressionNode(expressionNode);
             returnNode.setExpressionNode(expressionNode);
 
 
             blockNode.addStatementNode(returnNode);
             blockNode.addStatementNode(returnNode);
@@ -206,14 +244,14 @@ public class SFunction extends ANode {
 
 
         functionNode.setBlockNode(blockNode);
         functionNode.setBlockNode(blockNode);
 
 
-        functionNode.setLocation(location);
-        functionNode.setName(name);
+        functionNode.setLocation(getLocation());
+        functionNode.setName(functionName);
         functionNode.setReturnType(returnType);
         functionNode.setReturnType(returnType);
         functionNode.getTypeParameters().addAll(typeParameters);
         functionNode.getTypeParameters().addAll(typeParameters);
-        functionNode.getParameterNames().addAll(paramNameStrs);
+        functionNode.getParameterNames().addAll(parameterNames);
         functionNode.setStatic(isStatic);
         functionNode.setStatic(isStatic);
         functionNode.setVarArgs(false);
         functionNode.setVarArgs(false);
-        functionNode.setSynthetic(synthetic);
+        functionNode.setSynthetic(isSynthetic);
         functionNode.setMaxLoopCounter(maxLoopCounter);
         functionNode.setMaxLoopCounter(maxLoopCounter);
 
 
         return functionNode;
         return functionNode;

+ 20 - 12
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIf.java

@@ -35,14 +35,22 @@ import java.util.Objects;
  */
  */
 public class SIf extends AStatement {
 public class SIf extends AStatement {
 
 
-    protected final AExpression condition;
-    protected final SBlock ifblock;
+    private final AExpression conditionNode;
+    private final SBlock ifblockNode;
 
 
-    public SIf(Location location, AExpression condition, SBlock ifblock) {
-        super(location);
+    public SIf(int identifier, Location location, AExpression conditionNode, SBlock ifblockNode) {
+        super(identifier, location);
 
 
-        this.condition = Objects.requireNonNull(condition);
-        this.ifblock = ifblock;
+        this.conditionNode = Objects.requireNonNull(conditionNode);
+        this.ifblockNode = ifblockNode;
+    }
+
+    public AExpression getConditionNode() {
+        return conditionNode;
+    }
+
+    public SBlock getIfblockNode() {
+        return ifblockNode;
     }
     }
 
 
     @Override
     @Override
@@ -51,15 +59,15 @@ public class SIf extends AStatement {
 
 
         AExpression.Input conditionInput = new AExpression.Input();
         AExpression.Input conditionInput = new AExpression.Input();
         conditionInput.expected = boolean.class;
         conditionInput.expected = boolean.class;
-        AExpression.Output conditionOutput = AExpression.analyze(condition, classNode, scriptRoot, scope, conditionInput);
-        PainlessCast conditionCast = AnalyzerCaster.getLegalCast(condition.location,
+        AExpression.Output conditionOutput = AExpression.analyze(conditionNode, classNode, scriptRoot, scope, conditionInput);
+        PainlessCast conditionCast = AnalyzerCaster.getLegalCast(conditionNode.getLocation(),
                 conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
                 conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
 
 
-        if (condition.getChildIf(EBoolean.class) != null) {
+        if (conditionNode instanceof EBoolean) {
             throw createError(new IllegalArgumentException("Extraneous if statement."));
             throw createError(new IllegalArgumentException("Extraneous if statement."));
         }
         }
 
 
-        if (ifblock == null) {
+        if (ifblockNode == null) {
             throw createError(new IllegalArgumentException("Extraneous if statement."));
             throw createError(new IllegalArgumentException("Extraneous if statement."));
         }
         }
 
 
@@ -68,7 +76,7 @@ public class SIf extends AStatement {
         ifblockInput.inLoop = input.inLoop;
         ifblockInput.inLoop = input.inLoop;
         ifblockInput.lastLoop = input.lastLoop;
         ifblockInput.lastLoop = input.lastLoop;
 
 
-        Output ifblockOutput = ifblock.analyze(classNode, scriptRoot, scope.newLocalScope(), ifblockInput);
+        Output ifblockOutput = ifblockNode.analyze(classNode, scriptRoot, scope.newLocalScope(), ifblockInput);
 
 
         output.anyContinue = ifblockOutput.anyContinue;
         output.anyContinue = ifblockOutput.anyContinue;
         output.anyBreak = ifblockOutput.anyBreak;
         output.anyBreak = ifblockOutput.anyBreak;
@@ -77,7 +85,7 @@ public class SIf extends AStatement {
         IfNode ifNode = new IfNode();
         IfNode ifNode = new IfNode();
         ifNode.setConditionNode(AExpression.cast(conditionOutput.expressionNode, conditionCast));
         ifNode.setConditionNode(AExpression.cast(conditionOutput.expressionNode, conditionCast));
         ifNode.setBlockNode((BlockNode)ifblockOutput.statementNode);
         ifNode.setBlockNode((BlockNode)ifblockOutput.statementNode);
-        ifNode.setLocation(location);
+        ifNode.setLocation(getLocation());
 
 
         output.statementNode = ifNode;
         output.statementNode = ifNode;
 
 

+ 28 - 16
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIfElse.java

@@ -35,16 +35,28 @@ import java.util.Objects;
  */
  */
 public class SIfElse extends AStatement {
 public class SIfElse extends AStatement {
 
 
-    protected final AExpression condition;
-    protected final SBlock ifblock;
-    protected final SBlock elseblock;
+    private final AExpression conditionNode;
+    private final SBlock ifblockNode;
+    private final SBlock elseblockNode;
 
 
-    public SIfElse(Location location, AExpression condition, SBlock ifblock, SBlock elseblock) {
-        super(location);
+    public SIfElse(int identifier, Location location, AExpression conditionNode, SBlock ifblockNode, SBlock elseblockNode) {
+        super(identifier, location);
 
 
-        this.condition = Objects.requireNonNull(condition);
-        this.ifblock = ifblock;
-        this.elseblock = elseblock;
+        this.conditionNode = Objects.requireNonNull(conditionNode);
+        this.ifblockNode = ifblockNode;
+        this.elseblockNode = elseblockNode;
+    }
+
+    public AExpression getConditionNode() {
+        return conditionNode;
+    }
+
+    public SBlock getIfblockNode() {
+        return ifblockNode;
+    }
+
+    public SBlock getElseblockNode() {
+        return elseblockNode;
     }
     }
 
 
     @Override
     @Override
@@ -53,16 +65,16 @@ public class SIfElse extends AStatement {
 
 
         AExpression.Input conditionInput = new AExpression.Input();
         AExpression.Input conditionInput = new AExpression.Input();
         conditionInput.expected = boolean.class;
         conditionInput.expected = boolean.class;
-        AExpression.Output conditionOutput = AExpression.analyze(condition, classNode, scriptRoot, scope, conditionInput);
-        PainlessCast conditionCast = AnalyzerCaster.getLegalCast(condition.location,
+        AExpression.Output conditionOutput = AExpression.analyze(conditionNode, classNode, scriptRoot, scope, conditionInput);
+        PainlessCast conditionCast = AnalyzerCaster.getLegalCast(conditionNode.getLocation(),
                 conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
                 conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
 
 
 
 
-        if (condition.getChildIf(EBoolean.class) != null) {
+        if (conditionNode instanceof EBoolean) {
             throw createError(new IllegalArgumentException("Extraneous if statement."));
             throw createError(new IllegalArgumentException("Extraneous if statement."));
         }
         }
 
 
-        if (ifblock == null) {
+        if (ifblockNode == null) {
             throw createError(new IllegalArgumentException("Extraneous if statement."));
             throw createError(new IllegalArgumentException("Extraneous if statement."));
         }
         }
 
 
@@ -71,13 +83,13 @@ public class SIfElse extends AStatement {
         ifblockInput.inLoop = input.inLoop;
         ifblockInput.inLoop = input.inLoop;
         ifblockInput.lastLoop = input.lastLoop;
         ifblockInput.lastLoop = input.lastLoop;
 
 
-        Output ifblockOutput = ifblock.analyze(classNode, scriptRoot, scope.newLocalScope(), ifblockInput);
+        Output ifblockOutput = ifblockNode.analyze(classNode, scriptRoot, scope.newLocalScope(), ifblockInput);
 
 
         output.anyContinue = ifblockOutput.anyContinue;
         output.anyContinue = ifblockOutput.anyContinue;
         output.anyBreak = ifblockOutput.anyBreak;
         output.anyBreak = ifblockOutput.anyBreak;
         output.statementCount = ifblockOutput.statementCount;
         output.statementCount = ifblockOutput.statementCount;
 
 
-        if (elseblock == null) {
+        if (elseblockNode == null) {
             throw createError(new IllegalArgumentException("Extraneous else statement."));
             throw createError(new IllegalArgumentException("Extraneous else statement."));
         }
         }
 
 
@@ -86,7 +98,7 @@ public class SIfElse extends AStatement {
         elseblockInput.inLoop = input.inLoop;
         elseblockInput.inLoop = input.inLoop;
         elseblockInput.lastLoop = input.lastLoop;
         elseblockInput.lastLoop = input.lastLoop;
 
 
-        Output elseblockOutput = elseblock.analyze(classNode, scriptRoot, scope.newLocalScope(), elseblockInput);
+        Output elseblockOutput = elseblockNode.analyze(classNode, scriptRoot, scope.newLocalScope(), elseblockInput);
 
 
         output.methodEscape = ifblockOutput.methodEscape && elseblockOutput.methodEscape;
         output.methodEscape = ifblockOutput.methodEscape && elseblockOutput.methodEscape;
         output.loopEscape = ifblockOutput.loopEscape && elseblockOutput.loopEscape;
         output.loopEscape = ifblockOutput.loopEscape && elseblockOutput.loopEscape;
@@ -99,7 +111,7 @@ public class SIfElse extends AStatement {
         ifElseNode.setConditionNode(AExpression.cast(conditionOutput.expressionNode, conditionCast));
         ifElseNode.setConditionNode(AExpression.cast(conditionOutput.expressionNode, conditionCast));
         ifElseNode.setBlockNode((BlockNode)ifblockOutput.statementNode);
         ifElseNode.setBlockNode((BlockNode)ifblockOutput.statementNode);
         ifElseNode.setElseBlockNode((BlockNode)elseblockOutput.statementNode);
         ifElseNode.setElseBlockNode((BlockNode)elseblockOutput.statementNode);
-        ifElseNode.setLocation(location);
+        ifElseNode.setLocation(getLocation());
 
 
         output.statementNode = ifElseNode;
         output.statementNode = ifElseNode;
 
 

+ 14 - 10
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SReturn.java

@@ -33,12 +33,16 @@ import org.elasticsearch.painless.symbol.ScriptRoot;
  */
  */
 public class SReturn extends AStatement {
 public class SReturn extends AStatement {
 
 
-    protected final AExpression expression;
+    private final AExpression expressionNode;
 
 
-    public SReturn(Location location, AExpression expression) {
-        super(location);
+    public SReturn(int identifier, Location location, AExpression expressionNode) {
+        super(identifier, location);
 
 
-        this.expression = expression;
+        this.expressionNode = expressionNode;
+    }
+
+    public AExpression getExpressionNode() {
+        return expressionNode;
     }
     }
 
 
     @Override
     @Override
@@ -48,9 +52,9 @@ public class SReturn extends AStatement {
         AExpression.Output expressionOutput = null;
         AExpression.Output expressionOutput = null;
         PainlessCast expressionCast = null;
         PainlessCast expressionCast = null;
 
 
-        if (expression == null) {
+        if (expressionNode == null) {
             if (scope.getReturnType() != void.class) {
             if (scope.getReturnType() != void.class) {
-                throw location.createError(new ClassCastException("Cannot cast from " +
+                throw getLocation().createError(new ClassCastException("Cannot cast from " +
                         "[" + scope.getReturnCanonicalTypeName() + "] to " +
                         "[" + scope.getReturnCanonicalTypeName() + "] to " +
                         "[" + PainlessLookupUtility.typeToCanonicalTypeName(void.class) + "]."));
                         "[" + PainlessLookupUtility.typeToCanonicalTypeName(void.class) + "]."));
             }
             }
@@ -58,8 +62,8 @@ public class SReturn extends AStatement {
             AExpression.Input expressionInput = new AExpression.Input();
             AExpression.Input expressionInput = new AExpression.Input();
             expressionInput.expected = scope.getReturnType();
             expressionInput.expected = scope.getReturnType();
             expressionInput.internal = true;
             expressionInput.internal = true;
-            expressionOutput = AExpression.analyze(expression, classNode, scriptRoot, scope, expressionInput);
-            expressionCast = AnalyzerCaster.getLegalCast(expression.location,
+            expressionOutput = AExpression.analyze(expressionNode, classNode, scriptRoot, scope, expressionInput);
+            expressionCast = AnalyzerCaster.getLegalCast(expressionNode.getLocation(),
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal);
                     expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal);
         }
         }
 
 
@@ -70,8 +74,8 @@ public class SReturn extends AStatement {
         output.statementCount = 1;
         output.statementCount = 1;
 
 
         ReturnNode returnNode = new ReturnNode();
         ReturnNode returnNode = new ReturnNode();
-        returnNode.setExpressionNode(expression == null ? null : AExpression.cast(expressionOutput.expressionNode, expressionCast));
-        returnNode.setLocation(location);
+        returnNode.setExpressionNode(expressionNode == null ? null : AExpression.cast(expressionOutput.expressionNode, expressionCast));
+        returnNode.setLocation(getLocation());
 
 
         output.statementNode = returnNode;
         output.statementNode = returnNode;
 
 

+ 11 - 7
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SThrow.java

@@ -34,12 +34,16 @@ import java.util.Objects;
  */
  */
 public class SThrow extends AStatement {
 public class SThrow extends AStatement {
 
 
-    protected final AExpression expression;
+    private final AExpression expressionNode;
 
 
-    public SThrow(Location location, AExpression expression) {
-        super(location);
+    public SThrow(int identifier, Location location, AExpression expressionNode) {
+        super(identifier, location);
 
 
-        this.expression = Objects.requireNonNull(expression);
+        this.expressionNode = Objects.requireNonNull(expressionNode);
+    }
+
+    public AExpression getExpressionNode() {
+        return expressionNode;
     }
     }
 
 
     @Override
     @Override
@@ -48,8 +52,8 @@ public class SThrow extends AStatement {
 
 
         AExpression.Input expressionInput = new AExpression.Input();
         AExpression.Input expressionInput = new AExpression.Input();
         expressionInput.expected = Exception.class;
         expressionInput.expected = Exception.class;
-        AExpression.Output expressionOutput = AExpression.analyze(expression, classNode, scriptRoot, scope, expressionInput);
-        PainlessCast expressionCast = AnalyzerCaster.getLegalCast(expression.location,
+        AExpression.Output expressionOutput = AExpression.analyze(expressionNode, classNode, scriptRoot, scope, expressionInput);
+        PainlessCast expressionCast = AnalyzerCaster.getLegalCast(expressionNode.getLocation(),
                 expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal);
                 expressionOutput.actual, expressionInput.expected, expressionInput.explicit, expressionInput.internal);
 
 
         output.methodEscape = true;
         output.methodEscape = true;
@@ -59,7 +63,7 @@ public class SThrow extends AStatement {
 
 
         ThrowNode throwNode = new ThrowNode();
         ThrowNode throwNode = new ThrowNode();
         throwNode.setExpressionNode(AExpression.cast(expressionOutput.expressionNode, expressionCast));
         throwNode.setExpressionNode(AExpression.cast(expressionOutput.expressionNode, expressionCast));
-        throwNode.setLocation(location);
+        throwNode.setLocation(getLocation());
 
 
         output.statementNode = throwNode;
         output.statementNode = throwNode;
 
 

+ 11 - 10
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/STry.java

@@ -30,27 +30,28 @@ import org.elasticsearch.painless.symbol.ScriptRoot;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
+import java.util.Objects;
 
 
 /**
 /**
  * Represents the try block as part of a try-catch block.
  * Represents the try block as part of a try-catch block.
  */
  */
 public class STry extends AStatement {
 public class STry extends AStatement {
 
 
-    protected final SBlock block;
-    protected final List<SCatch> catches;
+    private final SBlock blockNode;
+    private final List<SCatch> catcheNodes;
 
 
-    public STry(Location location, SBlock block, List<SCatch> catches) {
-        super(location);
+    public STry(int identifier, Location location, SBlock blockNode, List<SCatch> catcheNodes) {
+        super(identifier, location);
 
 
-        this.block = block;
-        this.catches = Collections.unmodifiableList(catches);
+        this.blockNode = blockNode;
+        this.catcheNodes = Collections.unmodifiableList(Objects.requireNonNull(catcheNodes));
     }
     }
 
 
     @Override
     @Override
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
     Output analyze(ClassNode classNode, ScriptRoot scriptRoot, Scope scope, Input input) {
         Output output = new Output();
         Output output = new Output();
 
 
-        if (block == null) {
+        if (blockNode == null) {
             throw createError(new IllegalArgumentException("Extraneous try statement."));
             throw createError(new IllegalArgumentException("Extraneous try statement."));
         }
         }
 
 
@@ -59,7 +60,7 @@ public class STry extends AStatement {
         blockInput.inLoop = input.inLoop;
         blockInput.inLoop = input.inLoop;
         blockInput.lastLoop = input.lastLoop;
         blockInput.lastLoop = input.lastLoop;
 
 
-        Output blockOutput = block.analyze(classNode, scriptRoot, scope.newLocalScope(), blockInput);
+        Output blockOutput = blockNode.analyze(classNode, scriptRoot, scope.newLocalScope(), blockInput);
 
 
         output.methodEscape = blockOutput.methodEscape;
         output.methodEscape = blockOutput.methodEscape;
         output.loopEscape = blockOutput.loopEscape;
         output.loopEscape = blockOutput.loopEscape;
@@ -71,7 +72,7 @@ public class STry extends AStatement {
 
 
         List<Output> catchOutputs = new ArrayList<>();
         List<Output> catchOutputs = new ArrayList<>();
 
 
-        for (SCatch catc : catches) {
+        for (SCatch catc : catcheNodes) {
             Input catchInput = new Input();
             Input catchInput = new Input();
             catchInput.lastSource = input.lastSource;
             catchInput.lastSource = input.lastSource;
             catchInput.inLoop = input.inLoop;
             catchInput.inLoop = input.inLoop;
@@ -99,7 +100,7 @@ public class STry extends AStatement {
         }
         }
 
 
         tryNode.setBlockNode((BlockNode)blockOutput.statementNode);
         tryNode.setBlockNode((BlockNode)blockOutput.statementNode);
-        tryNode.setLocation(location);
+        tryNode.setLocation(getLocation());
 
 
         output.statementNode = tryNode;
         output.statementNode = tryNode;
 
 

+ 22 - 14
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SWhile.java

@@ -35,14 +35,22 @@ import java.util.Objects;
  */
  */
 public class SWhile extends AStatement {
 public class SWhile extends AStatement {
 
 
-    protected final AExpression condition;
-    protected final SBlock block;
+    private final AExpression conditionNode;
+    private final SBlock blockNode;
 
 
-    public SWhile(Location location, AExpression condition, SBlock block) {
-        super(location);
+    public SWhile(int identifier, Location location, AExpression conditionNode, SBlock blockNode) {
+        super(identifier, location);
 
 
-        this.condition = Objects.requireNonNull(condition);
-        this.block = block;
+        this.conditionNode = Objects.requireNonNull(conditionNode);
+        this.blockNode = blockNode;
+    }
+
+    public AExpression getConditionNode() {
+        return conditionNode;
+    }
+
+    public SBlock getBlockNode() {
+        return blockNode;
     }
     }
 
 
     @Override
     @Override
@@ -52,33 +60,33 @@ public class SWhile extends AStatement {
 
 
         AExpression.Input conditionInput = new AExpression.Input();
         AExpression.Input conditionInput = new AExpression.Input();
         conditionInput.expected = boolean.class;
         conditionInput.expected = boolean.class;
-        AExpression.Output conditionOutput = AExpression.analyze(condition, classNode, scriptRoot, scope, conditionInput);
-        PainlessCast conditionCast = AnalyzerCaster.getLegalCast(condition.location,
+        AExpression.Output conditionOutput = AExpression.analyze(conditionNode, classNode, scriptRoot, scope, conditionInput);
+        PainlessCast conditionCast = AnalyzerCaster.getLegalCast(conditionNode.getLocation(),
                 conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
                 conditionOutput.actual, conditionInput.expected, conditionInput.explicit, conditionInput.internal);
 
 
 
 
         boolean continuous = false;
         boolean continuous = false;
 
 
-        if (condition.getChildIf(EBoolean.class) != null) {
-            continuous = ((EBoolean)condition).constant;
+        if (conditionNode instanceof EBoolean) {
+            continuous = ((EBoolean)conditionNode).getBool();
 
 
             if (!continuous) {
             if (!continuous) {
                 throw createError(new IllegalArgumentException("Extraneous while loop."));
                 throw createError(new IllegalArgumentException("Extraneous while loop."));
             }
             }
 
 
-            if (block == null) {
+            if (blockNode == null) {
                 throw createError(new IllegalArgumentException("While loop has no escape."));
                 throw createError(new IllegalArgumentException("While loop has no escape."));
             }
             }
         }
         }
 
 
         Output blockOutput = null;
         Output blockOutput = null;
 
 
-        if (block != null) {
+        if (blockNode != null) {
             Input blockInput = new Input();
             Input blockInput = new Input();
             blockInput.beginLoop = true;
             blockInput.beginLoop = true;
             blockInput.inLoop = true;
             blockInput.inLoop = true;
 
 
-            blockOutput = block.analyze(classNode, scriptRoot, scope, blockInput);
+            blockOutput = blockNode.analyze(classNode, scriptRoot, scope, blockInput);
 
 
             if (blockOutput.loopEscape && blockOutput.anyContinue == false) {
             if (blockOutput.loopEscape && blockOutput.anyContinue == false) {
                 throw createError(new IllegalArgumentException("Extraneous while loop."));
                 throw createError(new IllegalArgumentException("Extraneous while loop."));
@@ -97,7 +105,7 @@ public class SWhile extends AStatement {
         WhileNode whileNode = new WhileNode();
         WhileNode whileNode = new WhileNode();
         whileNode.setConditionNode(AExpression.cast(conditionOutput.expressionNode, conditionCast));
         whileNode.setConditionNode(AExpression.cast(conditionOutput.expressionNode, conditionCast));
         whileNode.setBlockNode(blockOutput == null ? null : (BlockNode)blockOutput.statementNode);
         whileNode.setBlockNode(blockOutput == null ? null : (BlockNode)blockOutput.statementNode);
-        whileNode.setLocation(location);
+        whileNode.setLocation(getLocation());
         whileNode.setContinuous(continuous);
         whileNode.setContinuous(continuous);
 
 
         output.statementNode = whileNode;
         output.statementNode = whileNode;

+ 1 - 1
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java

@@ -55,7 +55,7 @@
  * {@link org.elasticsearch.painless.node.ERegex} - Represents a regular expression constant.
  * {@link org.elasticsearch.painless.node.ERegex} - Represents a regular expression constant.
  * {@link org.elasticsearch.painless.node.EString} - Represents a string constant.
  * {@link org.elasticsearch.painless.node.EString} - Represents a string constant.
  * {@link org.elasticsearch.painless.node.EUnary} - Represents a unary math expression.
  * {@link org.elasticsearch.painless.node.EUnary} - Represents a unary math expression.
- * {@link org.elasticsearch.painless.node.EVariable} - Represents a variable load/store.
+ * {@link org.elasticsearch.painless.node.ESymbol} - Represents a variable load/store.
  * {@link org.elasticsearch.painless.node.EBrace} - Represents an array load/store and defers to a child subnode.
  * {@link org.elasticsearch.painless.node.EBrace} - Represents an array load/store and defers to a child subnode.
  * {@link org.elasticsearch.painless.node.ECall} - Represents a method call and defers to a child subnode.
  * {@link org.elasticsearch.painless.node.ECall} - Represents a method call and defers to a child subnode.
  * {@link org.elasticsearch.painless.node.EDot} - Represents a field load/store and defers to a child subnode.
  * {@link org.elasticsearch.painless.node.EDot} - Represents a field load/store and defers to a child subnode.