Browse Source

Change compound assignment structure to support String concatenation (#61825)

This change modifies the compound assignment structure of the ir tree to support string 
concatenation in Java 8. The structure is changed so that when a StringBuilder is used for string 
concatenation it will be the first value pushed onto the stack prior loading the left-hand side of the 
compound assignment.
Jack Conradson 5 years ago
parent
commit
0216a71dda
15 changed files with 171 additions and 209 deletions
  1. 49 0
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/BinaryImplNode.java
  2. 1 24
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/BinaryNode.java
  3. 0 38
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreAccessNode.java
  4. 2 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreBraceDefNode.java
  5. 2 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreBraceNode.java
  6. 2 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreDotDefNode.java
  7. 2 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreDotNode.java
  8. 2 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreDotShortcutNode.java
  9. 2 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreListShortcutNode.java
  10. 2 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreMapShortcutNode.java
  11. 4 11
      modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/DefaultConstantFoldingOptimizationPhase.java
  12. 85 97
      modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/DefaultUserTreeToIRTreePhase.java
  13. 3 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/IRTreeBaseVisitor.java
  14. 2 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/IRTreeVisitor.java
  15. 13 13
      modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/PainlessUserTreeToIRTreePhase.java

+ 49 - 0
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/BinaryImplNode.java

@@ -0,0 +1,49 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.painless.ir;
+
+import org.elasticsearch.painless.ClassWriter;
+import org.elasticsearch.painless.MethodWriter;
+import org.elasticsearch.painless.phase.IRTreeVisitor;
+import org.elasticsearch.painless.symbol.WriteScope;
+
+public class BinaryImplNode extends BinaryNode {
+
+    /* ---- begin visitor ---- */
+
+    @Override
+    public <Scope> void visit(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
+        irTreeVisitor.visitBinaryImpl(this, scope);
+    }
+
+    @Override
+    public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
+        getLeftNode().visit(irTreeVisitor, scope);
+        getRightNode().visit(irTreeVisitor, scope);
+    }
+
+    /* ---- end visitor ---- */
+
+    @Override
+    protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
+        getLeftNode().write(classWriter, methodWriter, writeScope);
+        getRightNode().write(classWriter, methodWriter, writeScope);
+    }
+}

+ 1 - 24
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/BinaryNode.java

@@ -19,12 +19,7 @@
 
 
 package org.elasticsearch.painless.ir;
 package org.elasticsearch.painless.ir;
 
 
-import org.elasticsearch.painless.ClassWriter;
-import org.elasticsearch.painless.MethodWriter;
-import org.elasticsearch.painless.phase.IRTreeVisitor;
-import org.elasticsearch.painless.symbol.WriteScope;
-
-public class BinaryNode extends ExpressionNode {
+public abstract class BinaryNode extends ExpressionNode {
 
 
     /* ---- begin tree structure ---- */
     /* ---- begin tree structure ---- */
 
 
@@ -49,22 +44,4 @@ public class BinaryNode extends ExpressionNode {
 
 
     /* ---- end tree structure, begin visitor ---- */
     /* ---- end tree structure, begin visitor ---- */
 
 
-    @Override
-    public <Scope> void visit(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
-        irTreeVisitor.visitBinary(this, scope);
-    }
-
-    @Override
-    public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
-        leftNode.visit(irTreeVisitor, scope);
-        rightNode.visit(irTreeVisitor, scope);
-    }
-
-    /* ---- end visitor ---- */
-
-    @Override
-    protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
-        leftNode.write(classWriter, methodWriter, writeScope);
-        rightNode.write(classWriter, methodWriter, writeScope);
-    }
 }
 }

+ 0 - 38
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreAccessNode.java

@@ -1,38 +0,0 @@
-/*
- * Licensed to Elasticsearch under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.elasticsearch.painless.ir;
-
-public abstract class StoreAccessNode extends StoreNode {
-
-    /* ---- begin tree structure ---- */
-
-    private ExpressionNode accessNode;
-
-    public void setAccessNode(ExpressionNode accessNode) {
-        this.accessNode = accessNode;
-    }
-
-    public ExpressionNode getAccessNode() {
-        return accessNode;
-    }
-
-    /* ---- end tree structure --- */
-
-}

+ 2 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreBraceDefNode.java

@@ -28,7 +28,7 @@ import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.Type;
 
 
-public class StoreBraceDefNode extends StoreAccessNode {
+public class StoreBraceDefNode extends StoreNode {
 
 
     /* ---- begin node data ---- */
     /* ---- begin node data ---- */
 
 
@@ -55,14 +55,13 @@ public class StoreBraceDefNode extends StoreAccessNode {
 
 
     @Override
     @Override
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
-        getAccessNode().visit(irTreeVisitor, scope);
+        // do nothing; terminal node
     }
     }
 
 
     /* ---- end visitor ---- */
     /* ---- end visitor ---- */
 
 
     @Override
     @Override
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
-        getAccessNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
 
 
         methodWriter.writeDebugInfo(location);
         methodWriter.writeDebugInfo(location);

+ 2 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreBraceNode.java

@@ -24,7 +24,7 @@ import org.elasticsearch.painless.MethodWriter;
 import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.elasticsearch.painless.symbol.WriteScope;
 
 
-public class StoreBraceNode extends StoreAccessNode {
+public class StoreBraceNode extends StoreNode {
 
 
     /* ---- begin visitor ---- */
     /* ---- begin visitor ---- */
 
 
@@ -35,14 +35,13 @@ public class StoreBraceNode extends StoreAccessNode {
 
 
     @Override
     @Override
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
-        getAccessNode().visit(irTreeVisitor, scope);
+        // do nothing; terminal node
     }
     }
 
 
     /* ---- end visitor ---- */
     /* ---- end visitor ---- */
 
 
     @Override
     @Override
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
-        getAccessNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
 
 
         methodWriter.writeDebugInfo(location);
         methodWriter.writeDebugInfo(location);

+ 2 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreDotDefNode.java

@@ -27,7 +27,7 @@ import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.Type;
 
 
-public class StoreDotDefNode extends StoreAccessNode {
+public class StoreDotDefNode extends StoreNode {
 
 
     /* ---- begin node data ---- */
     /* ---- begin node data ---- */
 
 
@@ -50,14 +50,13 @@ public class StoreDotDefNode extends StoreAccessNode {
 
 
     @Override
     @Override
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
-        getAccessNode().visit(irTreeVisitor, scope);
+        // do nothing; terminal node
     }
     }
 
 
     /* ---- end visitor ---- */
     /* ---- end visitor ---- */
 
 
     @Override
     @Override
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
-        getAccessNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
 
 
         methodWriter.writeDebugInfo(location);
         methodWriter.writeDebugInfo(location);

+ 2 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreDotNode.java

@@ -26,7 +26,7 @@ import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.Type;
 
 
-public class StoreDotNode extends StoreAccessNode {
+public class StoreDotNode extends StoreNode {
 
 
     /* ---- begin node data ---- */
     /* ---- begin node data ---- */
 
 
@@ -49,14 +49,13 @@ public class StoreDotNode extends StoreAccessNode {
 
 
     @Override
     @Override
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
-        getAccessNode().visit(irTreeVisitor, scope);
+        // do nothing; terminal node
     }
     }
 
 
     /* ---- end visitor ---- */
     /* ---- end visitor ---- */
 
 
     @Override
     @Override
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
-        getAccessNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
 
 
         methodWriter.writeDebugInfo(location);
         methodWriter.writeDebugInfo(location);

+ 2 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreDotShortcutNode.java

@@ -25,7 +25,7 @@ import org.elasticsearch.painless.lookup.PainlessMethod;
 import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.elasticsearch.painless.symbol.WriteScope;
 
 
-public class StoreDotShortcutNode extends StoreAccessNode {
+public class StoreDotShortcutNode extends StoreNode {
 
 
     /* ---- begin node data ---- */
     /* ---- begin node data ---- */
 
 
@@ -48,14 +48,13 @@ public class StoreDotShortcutNode extends StoreAccessNode {
 
 
     @Override
     @Override
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
-        getAccessNode().visit(irTreeVisitor, scope);
+        // do nothing; terminal node
     }
     }
 
 
     /* ---- end visitor ---- */
     /* ---- end visitor ---- */
 
 
     @Override
     @Override
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
-        getAccessNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
 
 
         methodWriter.writeDebugInfo(location);
         methodWriter.writeDebugInfo(location);

+ 2 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreListShortcutNode.java

@@ -25,7 +25,7 @@ import org.elasticsearch.painless.lookup.PainlessMethod;
 import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.elasticsearch.painless.symbol.WriteScope;
 
 
-public class StoreListShortcutNode extends StoreAccessNode {
+public class StoreListShortcutNode extends StoreNode {
 
 
     /* ---- begin node data ---- */
     /* ---- begin node data ---- */
 
 
@@ -48,14 +48,13 @@ public class StoreListShortcutNode extends StoreAccessNode {
 
 
     @Override
     @Override
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
-        getAccessNode().visit(irTreeVisitor, scope);
+        // do nothing; terminal node
     }
     }
 
 
     /* ---- end visitor ---- */
     /* ---- end visitor ---- */
 
 
     @Override
     @Override
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
-        getAccessNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
 
 
         methodWriter.writeDebugInfo(location);
         methodWriter.writeDebugInfo(location);

+ 2 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/StoreMapShortcutNode.java

@@ -25,7 +25,7 @@ import org.elasticsearch.painless.lookup.PainlessMethod;
 import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.elasticsearch.painless.symbol.WriteScope;
 
 
-public class StoreMapShortcutNode extends StoreAccessNode {
+public class StoreMapShortcutNode extends StoreNode {
 
 
     /* ---- begin node data ---- */
     /* ---- begin node data ---- */
 
 
@@ -48,14 +48,13 @@ public class StoreMapShortcutNode extends StoreAccessNode {
 
 
     @Override
     @Override
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
     public <Scope> void visitChildren(IRTreeVisitor<Scope> irTreeVisitor, Scope scope) {
-        getAccessNode().visit(irTreeVisitor, scope);
+        // do nothing; terminal node
     }
     }
 
 
     /* ---- end visitor ---- */
     /* ---- end visitor ---- */
 
 
     @Override
     @Override
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
     protected void write(ClassWriter classWriter, MethodWriter methodWriter, WriteScope writeScope) {
-        getAccessNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
         getChildNode().write(classWriter, methodWriter, writeScope);
 
 
         methodWriter.writeDebugInfo(location);
         methodWriter.writeDebugInfo(location);

+ 4 - 11
modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/DefaultConstantFoldingOptimizationPhase.java

@@ -22,7 +22,7 @@ package org.elasticsearch.painless.phase;
 import org.elasticsearch.painless.AnalyzerCaster;
 import org.elasticsearch.painless.AnalyzerCaster;
 import org.elasticsearch.painless.Operation;
 import org.elasticsearch.painless.Operation;
 import org.elasticsearch.painless.ir.BinaryMathNode;
 import org.elasticsearch.painless.ir.BinaryMathNode;
-import org.elasticsearch.painless.ir.BinaryNode;
+import org.elasticsearch.painless.ir.BinaryImplNode;
 import org.elasticsearch.painless.ir.BooleanNode;
 import org.elasticsearch.painless.ir.BooleanNode;
 import org.elasticsearch.painless.ir.CastNode;
 import org.elasticsearch.painless.ir.CastNode;
 import org.elasticsearch.painless.ir.ComparisonNode;
 import org.elasticsearch.painless.ir.ComparisonNode;
@@ -168,9 +168,9 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
     }
     }
 
 
     @Override
     @Override
-    public void visitBinary(BinaryNode irBinaryNode, Consumer<ExpressionNode> scope) {
-        irBinaryNode.getLeftNode().visit(this, irBinaryNode::setLeftNode);
-        irBinaryNode.getRightNode().visit(this, irBinaryNode::setRightNode);
+    public void visitBinaryImpl(BinaryImplNode irBinaryImplNode, Consumer<ExpressionNode> scope) {
+        irBinaryImplNode.getLeftNode().visit(this, irBinaryImplNode::setLeftNode);
+        irBinaryImplNode.getRightNode().visit(this, irBinaryImplNode::setRightNode);
     }
     }
 
 
     @Override
     @Override
@@ -739,31 +739,26 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
 
 
     @Override
     @Override
     public void visitStoreDotDef(StoreDotDefNode irStoreDotDefNode, Consumer<ExpressionNode> scope) {
     public void visitStoreDotDef(StoreDotDefNode irStoreDotDefNode, Consumer<ExpressionNode> scope) {
-        irStoreDotDefNode.getAccessNode().visit(this, irStoreDotDefNode::setAccessNode);
         irStoreDotDefNode.getChildNode().visit(this, irStoreDotDefNode::setChildNode);
         irStoreDotDefNode.getChildNode().visit(this, irStoreDotDefNode::setChildNode);
     }
     }
 
 
     @Override
     @Override
     public void visitStoreDot(StoreDotNode irStoreDotNode, Consumer<ExpressionNode> scope) {
     public void visitStoreDot(StoreDotNode irStoreDotNode, Consumer<ExpressionNode> scope) {
-        irStoreDotNode.getAccessNode().visit(this, irStoreDotNode::setAccessNode);
         irStoreDotNode.getChildNode().visit(this, irStoreDotNode::setChildNode);
         irStoreDotNode.getChildNode().visit(this, irStoreDotNode::setChildNode);
     }
     }
 
 
     @Override
     @Override
     public void visitStoreDotShortcut(StoreDotShortcutNode irDotSubShortcutNode, Consumer<ExpressionNode> scope) {
     public void visitStoreDotShortcut(StoreDotShortcutNode irDotSubShortcutNode, Consumer<ExpressionNode> scope) {
-        irDotSubShortcutNode.getAccessNode().visit(this, irDotSubShortcutNode::setAccessNode);
         irDotSubShortcutNode.getChildNode().visit(this, irDotSubShortcutNode::setChildNode);
         irDotSubShortcutNode.getChildNode().visit(this, irDotSubShortcutNode::setChildNode);
     }
     }
 
 
     @Override
     @Override
     public void visitStoreListShortcut(StoreListShortcutNode irStoreListShortcutNode, Consumer<ExpressionNode> scope) {
     public void visitStoreListShortcut(StoreListShortcutNode irStoreListShortcutNode, Consumer<ExpressionNode> scope) {
-        irStoreListShortcutNode.getAccessNode().visit(this, irStoreListShortcutNode::setAccessNode);
         irStoreListShortcutNode.getChildNode().visit(this, irStoreListShortcutNode::setChildNode);
         irStoreListShortcutNode.getChildNode().visit(this, irStoreListShortcutNode::setChildNode);
     }
     }
 
 
     @Override
     @Override
     public void visitStoreMapShortcut(StoreMapShortcutNode irStoreMapShortcutNode, Consumer<ExpressionNode> scope) {
     public void visitStoreMapShortcut(StoreMapShortcutNode irStoreMapShortcutNode, Consumer<ExpressionNode> scope) {
-        irStoreMapShortcutNode.getAccessNode().visit(this, irStoreMapShortcutNode::setAccessNode);
         irStoreMapShortcutNode.getChildNode().visit(this, irStoreMapShortcutNode::setChildNode);
         irStoreMapShortcutNode.getChildNode().visit(this, irStoreMapShortcutNode::setChildNode);
     }
     }
 
 
@@ -774,13 +769,11 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
 
 
     @Override
     @Override
     public void visitStoreBraceDef(StoreBraceDefNode irStoreBraceDefNode, Consumer<ExpressionNode> scope) {
     public void visitStoreBraceDef(StoreBraceDefNode irStoreBraceDefNode, Consumer<ExpressionNode> scope) {
-        irStoreBraceDefNode.getAccessNode().visit(this, irStoreBraceDefNode::setAccessNode);
         irStoreBraceDefNode.getChildNode().visit(this, irStoreBraceDefNode::setChildNode);
         irStoreBraceDefNode.getChildNode().visit(this, irStoreBraceDefNode::setChildNode);
     }
     }
 
 
     @Override
     @Override
     public void visitStoreBrace(StoreBraceNode irStoreBraceNode, Consumer<ExpressionNode> scope) {
     public void visitStoreBrace(StoreBraceNode irStoreBraceNode, Consumer<ExpressionNode> scope) {
-        irStoreBraceNode.getAccessNode().visit(this, irStoreBraceNode::setAccessNode);
         irStoreBraceNode.getChildNode().visit(this, irStoreBraceNode::setChildNode);
         irStoreBraceNode.getChildNode().visit(this, irStoreBraceNode::setChildNode);
     }
     }
 
 

+ 85 - 97
modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/DefaultUserTreeToIRTreePhase.java

@@ -23,8 +23,9 @@ import org.elasticsearch.painless.DefBootstrap;
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.MethodWriter;
 import org.elasticsearch.painless.MethodWriter;
 import org.elasticsearch.painless.Operation;
 import org.elasticsearch.painless.Operation;
+import org.elasticsearch.painless.WriterConstants;
+import org.elasticsearch.painless.ir.BinaryImplNode;
 import org.elasticsearch.painless.ir.BinaryMathNode;
 import org.elasticsearch.painless.ir.BinaryMathNode;
-import org.elasticsearch.painless.ir.BinaryNode;
 import org.elasticsearch.painless.ir.BlockNode;
 import org.elasticsearch.painless.ir.BlockNode;
 import org.elasticsearch.painless.ir.BooleanNode;
 import org.elasticsearch.painless.ir.BooleanNode;
 import org.elasticsearch.painless.ir.BreakNode;
 import org.elasticsearch.painless.ir.BreakNode;
@@ -80,7 +81,6 @@ import org.elasticsearch.painless.ir.ReturnNode;
 import org.elasticsearch.painless.ir.StatementExpressionNode;
 import org.elasticsearch.painless.ir.StatementExpressionNode;
 import org.elasticsearch.painless.ir.StatementNode;
 import org.elasticsearch.painless.ir.StatementNode;
 import org.elasticsearch.painless.ir.StaticNode;
 import org.elasticsearch.painless.ir.StaticNode;
-import org.elasticsearch.painless.ir.StoreAccessNode;
 import org.elasticsearch.painless.ir.StoreBraceDefNode;
 import org.elasticsearch.painless.ir.StoreBraceDefNode;
 import org.elasticsearch.painless.ir.StoreBraceNode;
 import org.elasticsearch.painless.ir.StoreBraceNode;
 import org.elasticsearch.painless.ir.StoreDotDefNode;
 import org.elasticsearch.painless.ir.StoreDotDefNode;
@@ -273,17 +273,17 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
             blockNode.addStatementNode(returnNode);
             blockNode.addStatementNode(returnNode);
 
 
-            BinaryNode irBinaryNode = new BinaryNode();
-            irBinaryNode.setLocation(internalLocation);
-            irBinaryNode.setExpressionType(CallSite.class);
+            BinaryImplNode irBinaryImplNode = new BinaryImplNode();
+            irBinaryImplNode.setLocation(internalLocation);
+            irBinaryImplNode.setExpressionType(CallSite.class);
 
 
-            returnNode.setExpressionNode(irBinaryNode);
+            returnNode.setExpressionNode(irBinaryImplNode);
 
 
             StaticNode staticNode = new StaticNode();
             StaticNode staticNode = new StaticNode();
             staticNode.setLocation(internalLocation);
             staticNode.setLocation(internalLocation);
             staticNode.setExpressionType(DefBootstrap.class);
             staticNode.setExpressionType(DefBootstrap.class);
 
 
-            irBinaryNode.setLeftNode(staticNode);
+            irBinaryImplNode.setLeftNode(staticNode);
 
 
             InvokeCallNode invokeCallNode = new InvokeCallNode();
             InvokeCallNode invokeCallNode = new InvokeCallNode();
             invokeCallNode.setLocation(internalLocation);
             invokeCallNode.setLocation(internalLocation);
@@ -316,7 +316,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             );
             );
             invokeCallNode.setBox(DefBootstrap.class);
             invokeCallNode.setBox(DefBootstrap.class);
 
 
-            irBinaryNode.setRightNode(invokeCallNode);
+            irBinaryImplNode.setRightNode(invokeCallNode);
 
 
             LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode();
             LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode();
             irLoadFieldMemberNode.setLocation(internalLocation);
             irLoadFieldMemberNode.setLocation(internalLocation);
@@ -427,89 +427,67 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
     protected ExpressionNode buildLoadStore(int accessDepth, Location location, boolean isNullSafe,
     protected ExpressionNode buildLoadStore(int accessDepth, Location location, boolean isNullSafe,
             ExpressionNode irPrefixNode, ExpressionNode irIndexNode, ExpressionNode irLoadNode, StoreNode irStoreNode) {
             ExpressionNode irPrefixNode, ExpressionNode irIndexNode, ExpressionNode irLoadNode, StoreNode irStoreNode) {
 
 
-        ExpressionNode irExpressionNode;
+        // build out the load structure for load/compound assignment or the store structure for just store
+        ExpressionNode irExpressionNode = irLoadNode != null ? irLoadNode : irStoreNode;
 
 
-        // this load/store is only a load (read)
-        if (irStoreNode == null) {
-            ExpressionNode irAccessNode;
+        if (irPrefixNode != null) {
+            // this load/store is a dot or brace load/store
 
 
-            // this load is a symbol or dot load with no index node
-            if (irIndexNode == null) {
-                irAccessNode = irLoadNode;
-            // this load is a dot or brace load with an index node
-            } else {
-                BinaryNode irBinaryNode = new BinaryNode();
-                irBinaryNode.setLocation(location);
-                irBinaryNode.setExpressionType(irLoadNode.getExpressionType());
-                irBinaryNode.setLeftNode(irIndexNode);
-                irBinaryNode.setRightNode(irLoadNode);
+            if (irIndexNode != null) {
+                // this load/store requires an index
+                BinaryImplNode binaryImplNode = new BinaryImplNode();
+                binaryImplNode.setLocation(location);
+                binaryImplNode.setExpressionType(void.class);
 
 
-                irAccessNode = irBinaryNode;
+                if (isNullSafe) {
+                    // the null-safe structure is slightly different from the standard structure since
+                    // both the index and expression are not written to the stack if the prefix is null
+                    binaryImplNode.setLeftNode(irIndexNode);
+                    binaryImplNode.setRightNode(irExpressionNode);
+                    irExpressionNode = binaryImplNode;
+                } else {
+                    binaryImplNode.setLeftNode(irPrefixNode);
+                    binaryImplNode.setRightNode(irIndexNode);
+                    irPrefixNode = binaryImplNode;
+                }
             }
             }
 
 
-            // this wraps the load if this is a null-safe operation
-            if (isNullSafe) {
-                NullSafeSubNode nullSafeSubNode = new NullSafeSubNode();
-                nullSafeSubNode.setChildNode(irAccessNode);
-                nullSafeSubNode.setLocation(location);
-                nullSafeSubNode.setExpressionType(irAccessNode.getExpressionType());
-                irAccessNode = nullSafeSubNode;
+            if (irLoadNode != null && irStoreNode != null) {
+                // this is a compound assignment and requires and additional dup to re-access the prefix
+                DupNode dupNode = new DupNode();
+                dupNode.setLocation(location);
+                dupNode.setExpressionType(void.class);
+                dupNode.setSize(accessDepth);
+                dupNode.setDepth(0);
+                dupNode.setChildNode(irPrefixNode);
+                irPrefixNode = dupNode;
             }
             }
 
 
-            // this load is a symbol access with no prefix
-            if (irPrefixNode == null) {
-                irExpressionNode = irAccessNode;
-            // this load is a dot or brace access with a prefix node
-            } else {
-                BinaryNode irParentNode = new BinaryNode();
-                irParentNode.setLocation(location);
-                irParentNode.setExpressionType(irLoadNode.getExpressionType());
-                irParentNode.setLeftNode(irPrefixNode);
-                irParentNode.setRightNode(irAccessNode);
-
-                irExpressionNode = irParentNode;
-            }
-        // this is a store (write) and possibly also a load (read) for compound assignment
-        } else {
-            ExpressionNode irAccessNode;
+            // build the structure to combine the prefix and the load/store
+            BinaryImplNode binaryImplNode = new BinaryImplNode();
+            binaryImplNode.setLocation(location);
+            binaryImplNode.setExpressionType(irExpressionNode.getExpressionType());
 
 
-            // this store is a symbol or dot store with no index node
-            if (irIndexNode == null) {
-                irAccessNode = irPrefixNode;
-            // this store is a dot or brace load with an index node
+            if (isNullSafe) {
+                // build the structure for a null safe load
+                NullSafeSubNode irNullSafeSubNode = new NullSafeSubNode();
+                irNullSafeSubNode.setLocation(location);
+                irNullSafeSubNode.setExpressionType(irExpressionNode.getExpressionType());
+                irNullSafeSubNode.setChildNode(irExpressionNode);
+                binaryImplNode.setLeftNode(irPrefixNode);
+                binaryImplNode.setRightNode(irNullSafeSubNode);
             } else {
             } else {
-                BinaryNode irBinaryNode = new BinaryNode();
-                irBinaryNode.setLocation(location);
-                irBinaryNode.setExpressionType(void.class);
-                irBinaryNode.setLeftNode(irPrefixNode);
-                irBinaryNode.setRightNode(irIndexNode);
-
-                irAccessNode = irBinaryNode;
+                // build the structure for a standard load/store
+                binaryImplNode.setLeftNode(irPrefixNode);
+                binaryImplNode.setRightNode(irExpressionNode);
             }
             }
 
 
-            // this is a simple store
-            if (irLoadNode == null) {
-                // this store is a dot or brace store
-                if (irAccessNode != null) {
-                    ((StoreAccessNode)irStoreNode).setAccessNode(irAccessNode);
-                }
-            // this is a compound assignment
-            } else {
-                // this store has a prefix node that we must dup for a load
-                if (irAccessNode != null) {
-                    DupNode dupNode = new DupNode();
-                    dupNode.setLocation(location);
-                    dupNode.setExpressionType(void.class);
-                    dupNode.setSize(accessDepth);
-                    dupNode.setDepth(0);
-                    dupNode.setChildNode(irAccessNode);
-
-                    ((StoreAccessNode)irStoreNode).setAccessNode(dupNode);
-                }
-
-                irStoreNode.setChildNode(irLoadNode);
-            }
+            irExpressionNode = binaryImplNode;
+        }
 
 
+        if (irLoadNode != null && irStoreNode != null) {
+            // this is a compound assignment and the store is the root
+            irStoreNode.setChildNode(irExpressionNode);
             irExpressionNode = irStoreNode;
             irExpressionNode = irStoreNode;
         }
         }
 
 
@@ -844,7 +822,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         Class<?> compoundType = scriptScope.hasDecoration(userAssignmentNode, CompoundType.class) ?
         Class<?> compoundType = scriptScope.hasDecoration(userAssignmentNode, CompoundType.class) ?
                 scriptScope.getDecoration(userAssignmentNode, CompoundType.class).getCompoundType() : null;
                 scriptScope.getDecoration(userAssignmentNode, CompoundType.class).getCompoundType() : null;
 
 
-        StoreNode irStoreNode;
+        ExpressionNode irAssignmentNode;
         // add a cast node if necessary for the value node for the assignment
         // add a cast node if necessary for the value node for the assignment
         ExpressionNode irValueNode = injectCast(userAssignmentNode.getRightNode(), scriptScope);
         ExpressionNode irValueNode = injectCast(userAssignmentNode.getRightNode(), scriptScope);
 
 
@@ -852,7 +830,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         if (compoundType != null) {
         if (compoundType != null) {
             boolean concatenate = userAssignmentNode.getOperation() == Operation.ADD && compoundType == String.class;
             boolean concatenate = userAssignmentNode.getOperation() == Operation.ADD && compoundType == String.class;
             scriptScope.setCondition(userAssignmentNode.getLeftNode(), Compound.class);
             scriptScope.setCondition(userAssignmentNode.getLeftNode(), Compound.class);
-            irStoreNode = (StoreNode)visit(userAssignmentNode.getLeftNode(), scriptScope);
+            StoreNode irStoreNode = (StoreNode)visit(userAssignmentNode.getLeftNode(), scriptScope);
             ExpressionNode irLoadNode = irStoreNode.getChildNode();
             ExpressionNode irLoadNode = irStoreNode.getChildNode();
             ExpressionNode irCompoundNode;
             ExpressionNode irCompoundNode;
 
 
@@ -862,6 +840,11 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 stringConcatenationNode.setLocation(irStoreNode.getLocation());
                 stringConcatenationNode.setLocation(irStoreNode.getLocation());
                 stringConcatenationNode.setExpressionType(String.class);
                 stringConcatenationNode.setExpressionType(String.class);
                 irCompoundNode = stringConcatenationNode;
                 irCompoundNode = stringConcatenationNode;
+
+                // must handle the StringBuilder case for java version <= 8
+                if (irLoadNode instanceof BinaryImplNode && WriterConstants.INDY_STRING_CONCAT_BOOTSTRAP_HANDLE == null) {
+                    ((DupNode)((BinaryImplNode)irLoadNode).getLeftNode()).setDepth(1);
+                }
             // handles when the operation is mathematical
             // handles when the operation is mathematical
             } else {
             } else {
                 BinaryMathNode irBinaryMathNode = new BinaryMathNode();
                 BinaryMathNode irBinaryMathNode = new BinaryMathNode();
@@ -938,9 +921,11 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 irBinaryMathNode.setLeftNode(irLoadNode);
                 irBinaryMathNode.setLeftNode(irLoadNode);
                 irBinaryMathNode.setRightNode(irValueNode);
                 irBinaryMathNode.setRightNode(irValueNode);
             }
             }
+
+            irAssignmentNode = irStoreNode;
         // handles a standard assignment
         // handles a standard assignment
         } else {
         } else {
-            irStoreNode = (StoreNode)visit(userAssignmentNode.getLeftNode(), scriptScope);
+            irAssignmentNode = (ExpressionNode)visit(userAssignmentNode.getLeftNode(), scriptScope);
 
 
             // the value is read from after the assignment
             // the value is read from after the assignment
             if (read) {
             if (read) {
@@ -952,14 +937,17 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 irDupNode.setSize(MethodWriter.getType(irValueNode.getExpressionType()).getSize());
                 irDupNode.setSize(MethodWriter.getType(irValueNode.getExpressionType()).getSize());
                 irDupNode.setDepth(accessDepth);
                 irDupNode.setDepth(accessDepth);
                 irDupNode.setChildNode(irValueNode);
                 irDupNode.setChildNode(irValueNode);
-
                 irValueNode = irDupNode;
                 irValueNode = irDupNode;
             }
             }
 
 
-            irStoreNode.setChildNode(irValueNode);
+            if (irAssignmentNode instanceof BinaryImplNode) {
+                ((StoreNode)((BinaryImplNode)irAssignmentNode).getRightNode()).setChildNode(irValueNode);
+            } else {
+                ((StoreNode)irAssignmentNode).setChildNode(irValueNode);
+            }
         }
         }
 
 
-        scriptScope.putDecoration(userAssignmentNode, new IRNodeDecoration(irStoreNode));
+        scriptScope.putDecoration(userAssignmentNode, new IRNodeDecoration(irAssignmentNode));
     }
     }
 
 
     @Override
     @Override
@@ -1289,17 +1277,17 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
             irStatementExpressionNode.setExpressionNode(irStoreFieldMemberNode);
             irStatementExpressionNode.setExpressionNode(irStoreFieldMemberNode);
 
 
-            BinaryNode irBinaryNode = new BinaryNode();
-            irBinaryNode.setLocation(userRegexNode.getLocation());
-            irBinaryNode.setExpressionType(Pattern.class);
+            BinaryImplNode irBinaryImplNode = new BinaryImplNode();
+            irBinaryImplNode.setLocation(userRegexNode.getLocation());
+            irBinaryImplNode.setExpressionType(Pattern.class);
 
 
-            irStoreFieldMemberNode.setChildNode(irBinaryNode);
+            irStoreFieldMemberNode.setChildNode(irBinaryImplNode);
 
 
             StaticNode irStaticNode = new StaticNode();
             StaticNode irStaticNode = new StaticNode();
             irStaticNode.setLocation(userRegexNode.getLocation());
             irStaticNode.setLocation(userRegexNode.getLocation());
             irStaticNode.setExpressionType(Pattern.class);
             irStaticNode.setExpressionType(Pattern.class);
 
 
-            irBinaryNode.setLeftNode(irStaticNode);
+            irBinaryImplNode.setLeftNode(irStaticNode);
 
 
             InvokeCallNode invokeCallNode = new InvokeCallNode();
             InvokeCallNode invokeCallNode = new InvokeCallNode();
             invokeCallNode.setLocation(userRegexNode.getLocation());
             invokeCallNode.setLocation(userRegexNode.getLocation());
@@ -1316,7 +1304,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                     )
                     )
             );
             );
 
 
-            irBinaryNode.setRightNode(invokeCallNode);
+            irBinaryImplNode.setRightNode(invokeCallNode);
 
 
             ConstantNode irConstantNode = new ConstantNode();
             ConstantNode irConstantNode = new ConstantNode();
             irConstantNode.setLocation(userRegexNode.getLocation());
             irConstantNode.setLocation(userRegexNode.getLocation());
@@ -1853,12 +1841,12 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             irExpressionNode = irNullSafeSubNode;
             irExpressionNode = irNullSafeSubNode;
         }
         }
 
 
-        BinaryNode irBinaryNode = new BinaryNode();
-        irBinaryNode.setLeftNode((ExpressionNode)visit(userCallNode.getPrefixNode(), scriptScope));
-        irBinaryNode.setRightNode(irExpressionNode);
-        irBinaryNode.setLocation(irExpressionNode.getLocation());
-        irBinaryNode.setExpressionType(irExpressionNode.getExpressionType());
+        BinaryImplNode irBinaryImplNode = new BinaryImplNode();
+        irBinaryImplNode.setLeftNode((ExpressionNode)visit(userCallNode.getPrefixNode(), scriptScope));
+        irBinaryImplNode.setRightNode(irExpressionNode);
+        irBinaryImplNode.setLocation(irExpressionNode.getLocation());
+        irBinaryImplNode.setExpressionType(irExpressionNode.getExpressionType());
 
 
-        scriptScope.putDecoration(userCallNode, new IRNodeDecoration(irBinaryNode));
+        scriptScope.putDecoration(userCallNode, new IRNodeDecoration(irBinaryImplNode));
     }
     }
 }
 }

+ 3 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/IRTreeBaseVisitor.java

@@ -20,7 +20,7 @@
 package org.elasticsearch.painless.phase;
 package org.elasticsearch.painless.phase;
 
 
 import org.elasticsearch.painless.ir.BinaryMathNode;
 import org.elasticsearch.painless.ir.BinaryMathNode;
-import org.elasticsearch.painless.ir.BinaryNode;
+import org.elasticsearch.painless.ir.BinaryImplNode;
 import org.elasticsearch.painless.ir.BlockNode;
 import org.elasticsearch.painless.ir.BlockNode;
 import org.elasticsearch.painless.ir.BooleanNode;
 import org.elasticsearch.painless.ir.BooleanNode;
 import org.elasticsearch.painless.ir.BreakNode;
 import org.elasticsearch.painless.ir.BreakNode;
@@ -196,8 +196,8 @@ public class IRTreeBaseVisitor<Scope> implements IRTreeVisitor<Scope> {
     }
     }
 
 
     @Override
     @Override
-    public void visitBinary(BinaryNode irBinaryNode, Scope scope) {
-        irBinaryNode.visitChildren(this, scope);
+    public void visitBinaryImpl(BinaryImplNode irBinaryImplNode, Scope scope) {
+        irBinaryImplNode.visitChildren(this, scope);
     }
     }
 
 
     @Override
     @Override

+ 2 - 2
modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/IRTreeVisitor.java

@@ -20,7 +20,7 @@
 package org.elasticsearch.painless.phase;
 package org.elasticsearch.painless.phase;
 
 
 import org.elasticsearch.painless.ir.BinaryMathNode;
 import org.elasticsearch.painless.ir.BinaryMathNode;
-import org.elasticsearch.painless.ir.BinaryNode;
+import org.elasticsearch.painless.ir.BinaryImplNode;
 import org.elasticsearch.painless.ir.BlockNode;
 import org.elasticsearch.painless.ir.BlockNode;
 import org.elasticsearch.painless.ir.BooleanNode;
 import org.elasticsearch.painless.ir.BooleanNode;
 import org.elasticsearch.painless.ir.BreakNode;
 import org.elasticsearch.painless.ir.BreakNode;
@@ -113,7 +113,7 @@ public interface IRTreeVisitor<Scope> {
     void visitContinue(ContinueNode irContinueNode, Scope scope);
     void visitContinue(ContinueNode irContinueNode, Scope scope);
     void visitBreak(BreakNode irBreakNode, Scope scope);
     void visitBreak(BreakNode irBreakNode, Scope scope);
 
 
-    void visitBinary(BinaryNode irBinaryNode, Scope scope);
+    void visitBinaryImpl(BinaryImplNode irBinaryImplNode, Scope scope);
     void visitUnaryMath(UnaryMathNode irUnaryMathNode, Scope scope);
     void visitUnaryMath(UnaryMathNode irUnaryMathNode, Scope scope);
     void visitBinaryMath(BinaryMathNode irBinaryMathNode, Scope scope);
     void visitBinaryMath(BinaryMathNode irBinaryMathNode, Scope scope);
     void visitStringConcatenation(StringConcatenationNode irStringConcatenationNode, Scope scope);
     void visitStringConcatenation(StringConcatenationNode irStringConcatenationNode, Scope scope);

+ 13 - 13
modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/PainlessUserTreeToIRTreePhase.java

@@ -24,7 +24,7 @@ import org.elasticsearch.painless.PainlessError;
 import org.elasticsearch.painless.PainlessExplainError;
 import org.elasticsearch.painless.PainlessExplainError;
 import org.elasticsearch.painless.ScriptClassInfo;
 import org.elasticsearch.painless.ScriptClassInfo;
 import org.elasticsearch.painless.ScriptClassInfo.MethodArgument;
 import org.elasticsearch.painless.ScriptClassInfo.MethodArgument;
-import org.elasticsearch.painless.ir.BinaryNode;
+import org.elasticsearch.painless.ir.BinaryImplNode;
 import org.elasticsearch.painless.ir.BlockNode;
 import org.elasticsearch.painless.ir.BlockNode;
 import org.elasticsearch.painless.ir.CatchNode;
 import org.elasticsearch.painless.ir.CatchNode;
 import org.elasticsearch.painless.ir.ConstantNode;
 import org.elasticsearch.painless.ir.ConstantNode;
@@ -405,18 +405,18 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
 
 
             irInvokeCallMemberNode.addArgumentNode(irLoadVariableNode);
             irInvokeCallMemberNode.addArgumentNode(irLoadVariableNode);
 
 
-            BinaryNode irBinaryNode = new BinaryNode();
-            irBinaryNode.setLocation(internalLocation);
-            irBinaryNode.setExpressionType(Map.class);
+            BinaryImplNode irBinaryImplNode = new BinaryImplNode();
+            irBinaryImplNode.setLocation(internalLocation);
+            irBinaryImplNode.setExpressionType(Map.class);
 
 
-            irInvokeCallMemberNode.addArgumentNode(irBinaryNode);
+            irInvokeCallMemberNode.addArgumentNode(irBinaryImplNode);
 
 
             irLoadVariableNode = new LoadVariableNode();
             irLoadVariableNode = new LoadVariableNode();
             irLoadVariableNode.setLocation(internalLocation);
             irLoadVariableNode.setLocation(internalLocation);
             irLoadVariableNode.setExpressionType(PainlessExplainError.class);
             irLoadVariableNode.setExpressionType(PainlessExplainError.class);
             irLoadVariableNode.setName("#painlessExplainError");
             irLoadVariableNode.setName("#painlessExplainError");
 
 
-            irBinaryNode.setLeftNode(irLoadVariableNode);
+            irBinaryImplNode.setLeftNode(irLoadVariableNode);
 
 
             InvokeCallNode irInvokeCallNode = new InvokeCallNode();
             InvokeCallNode irInvokeCallNode = new InvokeCallNode();
             irInvokeCallNode.setLocation(internalLocation);
             irInvokeCallNode.setLocation(internalLocation);
@@ -436,7 +436,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
                     )
                     )
             );
             );
 
 
-            irBinaryNode.setRightNode(irInvokeCallNode);
+            irBinaryImplNode.setRightNode(irInvokeCallNode);
 
 
             LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode();
             LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode();
             irLoadFieldMemberNode.setLocation(internalLocation);
             irLoadFieldMemberNode.setLocation(internalLocation);
@@ -492,17 +492,17 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
 
 
                 irInvokeCallMemberNode.addArgumentNode(irLoadVariableNode);
                 irInvokeCallMemberNode.addArgumentNode(irLoadVariableNode);
 
 
-                irBinaryNode = new BinaryNode();
-                irBinaryNode.setLocation(internalLocation);
-                irBinaryNode.setExpressionType(Map.class);
+                irBinaryImplNode = new BinaryImplNode();
+                irBinaryImplNode.setLocation(internalLocation);
+                irBinaryImplNode.setExpressionType(Map.class);
 
 
-                irInvokeCallMemberNode.addArgumentNode(irBinaryNode);
+                irInvokeCallMemberNode.addArgumentNode(irBinaryImplNode);
 
 
                 StaticNode irStaticNode = new StaticNode();
                 StaticNode irStaticNode = new StaticNode();
                 irStaticNode.setLocation(internalLocation);
                 irStaticNode.setLocation(internalLocation);
                 irStaticNode.setExpressionType(Collections.class);
                 irStaticNode.setExpressionType(Collections.class);
 
 
-                irBinaryNode.setLeftNode(irStaticNode);
+                irBinaryImplNode.setLeftNode(irStaticNode);
 
 
                 irInvokeCallNode = new InvokeCallNode();
                 irInvokeCallNode = new InvokeCallNode();
                 irInvokeCallNode.setLocation(internalLocation);
                 irInvokeCallNode.setLocation(internalLocation);
@@ -520,7 +520,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
                         )
                         )
                 );
                 );
 
 
-                irBinaryNode.setRightNode(irInvokeCallNode);
+                irBinaryImplNode.setRightNode(irInvokeCallNode);
             }
             }
 
 
             irBlockNode = new BlockNode();
             irBlockNode = new BlockNode();