Browse Source

Add decorations to ir nodes in Painless (#63894)

This change starts to switch all ir nodes to use decorations instead of specific member data. For this 
specific change, we add only an expression type decoration to begin with to keep the change smaller. 
Other members of the ir nodes will be converted to decorations in future changes.

The decoration system has two important advantages:
- The first is it's consistent with the user nodes as generated data is attached to them as a decoration, 
so we have a clear, consistent model for how to use both trees.
- This allows additionally generated data to be attached as necessary for optimization phases making 
the ir tree extendable which is one of our primary, long-term goals.
Jack Conradson 5 years ago
parent
commit
6eb168e640

+ 0 - 19
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/ExpressionNode.java

@@ -20,28 +20,9 @@
 package org.elasticsearch.painless.ir;
 package org.elasticsearch.painless.ir;
 
 
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.Location;
-import org.elasticsearch.painless.lookup.PainlessLookupUtility;
 
 
 public abstract class ExpressionNode extends IRNode {
 public abstract class ExpressionNode extends IRNode {
 
 
-    /* ---- begin node data ---- */
-
-    private Class<?> expressionType;
-
-    public void setExpressionType(Class<?> expressionType) {
-        this.expressionType = expressionType;
-    }
-
-    public Class<?> getExpressionType() {
-        return expressionType;
-    }
-
-    public String getExpressionCanonicalTypeName() {
-        return PainlessLookupUtility.typeToCanonicalTypeName(expressionType);
-    }
-
-    /* ---- end node data ---- */
-
     public ExpressionNode(Location location) {
     public ExpressionNode(Location location) {
         super(location);
         super(location);
     }
     }

+ 74 - 1
modules/lang-painless/src/main/java/org/elasticsearch/painless/ir/IRNode.java

@@ -22,9 +22,82 @@ package org.elasticsearch.painless.ir;
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.phase.IRTreeVisitor;
 import org.elasticsearch.painless.phase.IRTreeVisitor;
 
 
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
 public abstract class IRNode {
 public abstract class IRNode {
 
 
-    /* ---- begin node data ---- */
+    /* ---- begin decorations ---- */
+
+    public interface IRDecoration {
+
+    }
+
+    private final Map<Class<? extends IRDecoration>, IRDecoration> decorations = new HashMap<>();
+
+    @SuppressWarnings("unchecked")
+    public <T extends IRDecoration> T attachDecoration(T decoration) {
+        return (T)decorations.put(decoration.getClass(), decoration);
+    }
+
+    public <T extends IRDecoration> T removeDecoration(Class<T> type) {
+        return type.cast(decorations.remove(type));
+    }
+
+    public <T extends IRDecoration> T getDecoration(Class<T> type) {
+        return type.cast(decorations.get(type));
+    }
+
+    public boolean hasDecoration(Class<? extends IRDecoration> type) {
+        return decorations.containsKey(type);
+    }
+
+    public <T extends IRDecoration> boolean copyDecorationFrom(IRNode copyFromIRNode, Class<T> type) {
+        T decoration = copyFromIRNode.getDecoration(type);
+
+
+        if (decoration != null) {
+            attachDecoration(decoration);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /* ---- end decorations, begin conditions ---- */
+
+    public interface IRCondition {
+
+    }
+
+    private final Set<Class<? extends IRCondition>> conditions = new HashSet<>();
+
+    public boolean attachCondition(Class<? extends IRCondition> type) {
+        return conditions.add(type);
+    }
+
+    public boolean removeCondition(Class<? extends IRCondition> type) {
+        return conditions.remove(type);
+    }
+
+    public boolean hasCondition(Class<? extends IRCondition> type) {
+        return conditions.contains(type);
+    }
+
+    public boolean copyConditionFrom(IRNode copyFromIRNode, Class<? extends IRCondition> type) {
+        if (copyFromIRNode.hasCondition(type)) {
+            attachCondition(type);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /* ---- end conditions, begin node data ---- */
 
 
     private final Location location;
     private final Location location;
 
 

+ 17 - 15
modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/DefaultConstantFoldingOptimizationPhase.java

@@ -67,6 +67,7 @@ import org.elasticsearch.painless.ir.ThrowNode;
 import org.elasticsearch.painless.ir.UnaryMathNode;
 import org.elasticsearch.painless.ir.UnaryMathNode;
 import org.elasticsearch.painless.ir.WhileLoopNode;
 import org.elasticsearch.painless.ir.WhileLoopNode;
 import org.elasticsearch.painless.lookup.PainlessLookupUtility;
 import org.elasticsearch.painless.lookup.PainlessLookupUtility;
+import org.elasticsearch.painless.symbol.IRDecorations.IRDExpressionType;
 
 
 import java.util.function.Consumer;
 import java.util.function.Consumer;
 
 
@@ -180,7 +181,7 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
         if (irUnaryMathNode.getChildNode() instanceof ConstantNode) {
         if (irUnaryMathNode.getChildNode() instanceof ConstantNode) {
             ConstantNode irConstantNode = (ConstantNode)irUnaryMathNode.getChildNode();
             ConstantNode irConstantNode = (ConstantNode)irUnaryMathNode.getChildNode();
             Operation operation = irUnaryMathNode.getOperation();
             Operation operation = irUnaryMathNode.getOperation();
-            Class<?> type = irUnaryMathNode.getExpressionType();
+            Class<?> type = irUnaryMathNode.getDecoration(IRDExpressionType.class).getType();
 
 
             if (operation == Operation.SUB) {
             if (operation == Operation.SUB) {
                 if (type == int.class) {
                 if (type == int.class) {
@@ -238,7 +239,7 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
             ConstantNode irLeftConstantNode = (ConstantNode)irBinaryMathNode.getLeftNode();
             ConstantNode irLeftConstantNode = (ConstantNode)irBinaryMathNode.getLeftNode();
             ConstantNode irRightConstantNode = (ConstantNode)irBinaryMathNode.getRightNode();
             ConstantNode irRightConstantNode = (ConstantNode)irBinaryMathNode.getRightNode();
             Operation operation = irBinaryMathNode.getOperation();
             Operation operation = irBinaryMathNode.getOperation();
-            Class<?> type = irBinaryMathNode.getExpressionType();
+            Class<?> type = irBinaryMathNode.getDecoration(IRDExpressionType.class).getType();
 
 
             if (operation == Operation.MUL) {
             if (operation == Operation.MUL) {
                 if (type == int.class) {
                 if (type == int.class) {
@@ -435,22 +436,22 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
             if (irLeftNode instanceof ConstantNode && irRightNode instanceof ConstantNode) {
             if (irLeftNode instanceof ConstantNode && irRightNode instanceof ConstantNode) {
                 ConstantNode irConstantNode = (ConstantNode)irLeftNode;
                 ConstantNode irConstantNode = (ConstantNode)irLeftNode;
                 irConstantNode.setConstant("" + irConstantNode.getConstant() + ((ConstantNode)irRightNode).getConstant());
                 irConstantNode.setConstant("" + irConstantNode.getConstant() + ((ConstantNode)irRightNode).getConstant());
-                irConstantNode.setExpressionType(String.class);
+                irConstantNode.attachDecoration(new IRDExpressionType(String.class));
                 irStringConcatenationNode.getArgumentNodes().remove(i + 1);
                 irStringConcatenationNode.getArgumentNodes().remove(i + 1);
             } else if (irLeftNode instanceof NullNode && irRightNode instanceof ConstantNode) {
             } else if (irLeftNode instanceof NullNode && irRightNode instanceof ConstantNode) {
                 ConstantNode irConstantNode = (ConstantNode)irRightNode;
                 ConstantNode irConstantNode = (ConstantNode)irRightNode;
                 irConstantNode.setConstant("" + null + ((ConstantNode)irRightNode).getConstant());
                 irConstantNode.setConstant("" + null + ((ConstantNode)irRightNode).getConstant());
-                irConstantNode.setExpressionType(String.class);
+                irConstantNode.attachDecoration(new IRDExpressionType(String.class));
                 irStringConcatenationNode.getArgumentNodes().remove(i);
                 irStringConcatenationNode.getArgumentNodes().remove(i);
             } else if (irLeftNode instanceof ConstantNode && irRightNode instanceof NullNode) {
             } else if (irLeftNode instanceof ConstantNode && irRightNode instanceof NullNode) {
                 ConstantNode irConstantNode = (ConstantNode)irLeftNode;
                 ConstantNode irConstantNode = (ConstantNode)irLeftNode;
                 irConstantNode.setConstant("" + ((ConstantNode)irLeftNode).getConstant() + null);
                 irConstantNode.setConstant("" + ((ConstantNode)irLeftNode).getConstant() + null);
-                irConstantNode.setExpressionType(String.class);
+                irConstantNode.attachDecoration(new IRDExpressionType(String.class));
                 irStringConcatenationNode.getArgumentNodes().remove(i + 1);
                 irStringConcatenationNode.getArgumentNodes().remove(i + 1);
             } else if (irLeftNode instanceof NullNode && irRightNode instanceof NullNode) {
             } else if (irLeftNode instanceof NullNode && irRightNode instanceof NullNode) {
                 ConstantNode irConstantNode = new ConstantNode(irLeftNode.getLocation());
                 ConstantNode irConstantNode = new ConstantNode(irLeftNode.getLocation());
                 irConstantNode.setConstant("" + null + null);
                 irConstantNode.setConstant("" + null + null);
-                irConstantNode.setExpressionType(String.class);
+                irConstantNode.attachDecoration(new IRDExpressionType(String.class));
                 irStringConcatenationNode.getArgumentNodes().set(i, irConstantNode);
                 irStringConcatenationNode.getArgumentNodes().set(i, irConstantNode);
                 irStringConcatenationNode.getArgumentNodes().remove(i + 1);
                 irStringConcatenationNode.getArgumentNodes().remove(i + 1);
             } else {
             } else {
@@ -476,7 +477,7 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
             ConstantNode irLeftConstantNode = (ConstantNode)irBooleanNode.getLeftNode();
             ConstantNode irLeftConstantNode = (ConstantNode)irBooleanNode.getLeftNode();
             ConstantNode irRightConstantNode = (ConstantNode)irBooleanNode.getRightNode();
             ConstantNode irRightConstantNode = (ConstantNode)irBooleanNode.getRightNode();
             Operation operation = irBooleanNode.getOperation();
             Operation operation = irBooleanNode.getOperation();
-            Class<?> type = irBooleanNode.getExpressionType();
+            Class<?> type = irBooleanNode.getDecoration(IRDExpressionType.class).getType();
 
 
             if (operation == Operation.AND) {
             if (operation == Operation.AND) {
                 if (type == boolean.class) {
                 if (type == boolean.class) {
@@ -544,7 +545,7 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
                     }
                     }
                 }
                 }
 
 
-                irLeftConstantNode.setExpressionType(boolean.class);
+                irLeftConstantNode.attachDecoration(new IRDExpressionType(boolean.class));
                 scope.accept(irLeftConstantNode);
                 scope.accept(irLeftConstantNode);
             } else if (operation == Operation.NE || operation == Operation.NER) {
             } else if (operation == Operation.NE || operation == Operation.NER) {
                 if (type == boolean.class) {
                 if (type == boolean.class) {
@@ -571,7 +572,7 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
                     }
                     }
                 }
                 }
 
 
-                irLeftConstantNode.setExpressionType(boolean.class);
+                irLeftConstantNode.attachDecoration(new IRDExpressionType(boolean.class));
                 scope.accept(irLeftConstantNode);
                 scope.accept(irLeftConstantNode);
             } else if (irLeftConstantNode != null && irRightConstantNode != null) {
             } else if (irLeftConstantNode != null && irRightConstantNode != null) {
                 if (operation == Operation.GT) {
                 if (operation == Operation.GT) {
@@ -591,7 +592,7 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
                                 "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]"));
                                 "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]"));
                     }
                     }
 
 
-                    irLeftConstantNode.setExpressionType(boolean.class);
+                    irLeftConstantNode.attachDecoration(new IRDExpressionType(boolean.class));
                     scope.accept(irLeftConstantNode);
                     scope.accept(irLeftConstantNode);
                 } else if (operation == Operation.GTE) {
                 } else if (operation == Operation.GTE) {
                     if (type == int.class) {
                     if (type == int.class) {
@@ -610,7 +611,7 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
                                 "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]"));
                                 "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]"));
                     }
                     }
 
 
-                    irLeftConstantNode.setExpressionType(boolean.class);
+                    irLeftConstantNode.attachDecoration(new IRDExpressionType(boolean.class));
                     scope.accept(irLeftConstantNode);
                     scope.accept(irLeftConstantNode);
                 } else if (operation == Operation.LT) {
                 } else if (operation == Operation.LT) {
                     if (type == int.class) {
                     if (type == int.class) {
@@ -629,7 +630,7 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
                                 "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]"));
                                 "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]"));
                     }
                     }
 
 
-                    irLeftConstantNode.setExpressionType(boolean.class);
+                    irLeftConstantNode.attachDecoration(new IRDExpressionType(boolean.class));
                     scope.accept(irLeftConstantNode);
                     scope.accept(irLeftConstantNode);
                 } else if (operation == Operation.LTE) {
                 } else if (operation == Operation.LTE) {
                     if (type == int.class) {
                     if (type == int.class) {
@@ -648,7 +649,7 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
                                 "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]"));
                                 "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]"));
                     }
                     }
 
 
-                    irLeftConstantNode.setExpressionType(boolean.class);
+                    irLeftConstantNode.attachDecoration(new IRDExpressionType(boolean.class));
                     scope.accept(irLeftConstantNode);
                     scope.accept(irLeftConstantNode);
                 }
                 }
             }
             }
@@ -659,11 +660,12 @@ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<C
     public void visitCast(CastNode irCastNode, Consumer<ExpressionNode> scope) {
     public void visitCast(CastNode irCastNode, Consumer<ExpressionNode> scope) {
         irCastNode.getChildNode().visit(this, irCastNode::setChildNode);
         irCastNode.getChildNode().visit(this, irCastNode::setChildNode);
 
 
-        if (irCastNode.getChildNode() instanceof ConstantNode && PainlessLookupUtility.isConstantType(irCastNode.getExpressionType())) {
+        if (irCastNode.getChildNode() instanceof ConstantNode &&
+                PainlessLookupUtility.isConstantType(irCastNode.getDecoration(IRDExpressionType.class).getType())) {
             ConstantNode irConstantNode = (ConstantNode)irCastNode.getChildNode();
             ConstantNode irConstantNode = (ConstantNode)irCastNode.getChildNode();
             irConstantNode.setConstant(
             irConstantNode.setConstant(
                     AnalyzerCaster.constCast(irCastNode.getLocation(), irConstantNode.getConstant(), irCastNode.getCast()));
                     AnalyzerCaster.constCast(irCastNode.getLocation(), irConstantNode.getConstant(), irCastNode.getCast()));
-            irConstantNode.setExpressionType(irCastNode.getExpressionType());
+            irConstantNode.copyDecorationFrom(irCastNode, IRDExpressionType.class);
             scope.accept(irConstantNode);
             scope.accept(irConstantNode);
         }
         }
     }
     }

+ 49 - 39
modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/DefaultIRTreeToASMBytesPhase.java

@@ -106,6 +106,7 @@ import org.elasticsearch.painless.lookup.PainlessLookupUtility;
 import org.elasticsearch.painless.lookup.PainlessMethod;
 import org.elasticsearch.painless.lookup.PainlessMethod;
 import org.elasticsearch.painless.lookup.def;
 import org.elasticsearch.painless.lookup.def;
 import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction;
 import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction;
+import org.elasticsearch.painless.symbol.IRDecorations.IRDExpressionType;
 import org.elasticsearch.painless.symbol.ScriptScope;
 import org.elasticsearch.painless.symbol.ScriptScope;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.elasticsearch.painless.symbol.WriteScope;
 import org.elasticsearch.painless.symbol.WriteScope.Variable;
 import org.elasticsearch.painless.symbol.WriteScope.Variable;
@@ -405,7 +406,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
             ExpressionNode irExpressionNode = (ExpressionNode)irInitializerNode;
             ExpressionNode irExpressionNode = (ExpressionNode)irInitializerNode;
 
 
             visit(irExpressionNode, writeScope);
             visit(irExpressionNode, writeScope);
-            methodWriter.writePop(MethodWriter.getType(irExpressionNode.getExpressionType()).getSize());
+            methodWriter.writePop(MethodWriter.getType(irExpressionNode.getDecoration(IRDExpressionType.class).getType()).getSize());
         }
         }
 
 
         methodWriter.mark(start);
         methodWriter.mark(start);
@@ -431,7 +432,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         if (irAfterthoughtNode != null) {
         if (irAfterthoughtNode != null) {
             methodWriter.mark(begin);
             methodWriter.mark(begin);
             visit(irAfterthoughtNode, writeScope);
             visit(irAfterthoughtNode, writeScope);
-            methodWriter.writePop(MethodWriter.getType(irAfterthoughtNode.getExpressionType()).getSize());
+            methodWriter.writePop(MethodWriter.getType(irAfterthoughtNode.getDecoration(IRDExpressionType.class).getType()).getSize());
         }
         }
 
 
         if (irAfterthoughtNode != null || allEscape == false) {
         if (irAfterthoughtNode != null || allEscape == false) {
@@ -549,7 +550,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
 
 
         Variable variable = writeScope.defineVariable(irDeclarationNode.getDeclarationType(), irDeclarationNode.getName());
         Variable variable = writeScope.defineVariable(irDeclarationNode.getDeclarationType(), irDeclarationNode.getName());
 
 
-        if (irDeclarationNode.getExpressionNode() ==  null) {
+        if (irDeclarationNode.getExpressionNode() == null) {
             Class<?> sort = variable.getType();
             Class<?> sort = variable.getType();
 
 
             if (sort == void.class || sort == boolean.class || sort == byte.class ||
             if (sort == void.class || sort == boolean.class || sort == byte.class ||
@@ -588,7 +589,8 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         MethodWriter methodWriter = writeScope.getMethodWriter();
         MethodWriter methodWriter = writeScope.getMethodWriter();
         methodWriter.writeStatementOffset(irStatementExpressionNode.getLocation());
         methodWriter.writeStatementOffset(irStatementExpressionNode.getLocation());
         visit(irStatementExpressionNode.getExpressionNode(), writeScope);
         visit(irStatementExpressionNode.getExpressionNode(), writeScope);
-        Class<?> expressionType = irStatementExpressionNode.getExpressionNode().getExpressionType();
+
+        Class<?> expressionType = irStatementExpressionNode.getExpressionNode().getDecoration(IRDExpressionType.class).getType();
         Type asmExpressionType = MethodWriter.getType(expressionType);
         Type asmExpressionType = MethodWriter.getType(expressionType);
         methodWriter.writePop(asmExpressionType.getSize());
         methodWriter.writePop(asmExpressionType.getSize());
     }
     }
@@ -709,8 +711,8 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
                 defFlags |= DefBootstrap.OPERATOR_EXPLICIT_CAST;
                 defFlags |= DefBootstrap.OPERATOR_EXPLICIT_CAST;
             }
             }
 
 
-            Type actualType = MethodWriter.getType(irUnaryMathNode.getExpressionType());
-            Type childType = MethodWriter.getType(irUnaryMathNode.getChildNode().getExpressionType());
+            Type actualType = MethodWriter.getType(irUnaryMathNode.getDecoration(IRDExpressionType.class).getType());
+            Type childType = MethodWriter.getType(irUnaryMathNode.getChildNode().getDecoration(IRDExpressionType.class).getType());
 
 
             Class<?> unaryType = irUnaryMathNode.getUnaryType();
             Class<?> unaryType = irUnaryMathNode.getUnaryType();
 
 
@@ -725,7 +727,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
                         methodWriter.push(-1L);
                         methodWriter.push(-1L);
                     } else {
                     } else {
                         throw new IllegalStateException("unexpected unary math operation [" + operation + "] " +
                         throw new IllegalStateException("unexpected unary math operation [" + operation + "] " +
-                                "for type [" + irUnaryMathNode.getExpressionCanonicalTypeName() + "]");
+                                "for type [" + irUnaryMathNode.getDecoration(IRDExpressionType.class).getCanonicalTypeName() + "]");
                     }
                     }
 
 
                     methodWriter.math(MethodWriter.XOR, actualType);
                     methodWriter.math(MethodWriter.XOR, actualType);
@@ -744,7 +746,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
                 }
                 }
             } else {
             } else {
                 throw new IllegalStateException("unexpected unary math operation [" + operation + "] " +
                 throw new IllegalStateException("unexpected unary math operation [" + operation + "] " +
-                        "for type [" + irUnaryMathNode.getExpressionCanonicalTypeName() + "]");
+                        "for type [" + irUnaryMathNode.getDecoration(IRDExpressionType.class).getCanonicalTypeName() + "]");
             }
             }
         }
         }
     }
     }
@@ -770,18 +772,23 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
                 methodWriter.invokeVirtual(Type.getType(Matcher.class), WriterConstants.MATCHER_MATCHES);
                 methodWriter.invokeVirtual(Type.getType(Matcher.class), WriterConstants.MATCHER_MATCHES);
             } else {
             } else {
                 throw new IllegalStateException("unexpected binary math operation [" + operation + "] " +
                 throw new IllegalStateException("unexpected binary math operation [" + operation + "] " +
-                        "for type [" + irBinaryMathNode.getExpressionCanonicalTypeName() + "]");
+                        "for type [" + irBinaryMathNode.getDecoration(IRDExpressionType.class).getCanonicalTypeName() + "]");
             }
             }
         } else {
         } else {
             visit(irLeftNode, writeScope);
             visit(irLeftNode, writeScope);
             visit(irRightNode, writeScope);
             visit(irRightNode, writeScope);
 
 
+            Class<?> expressionType = irBinaryMathNode.getDecoration(IRDExpressionType.class).getType();
+
             if (irBinaryMathNode.getBinaryType() == def.class ||
             if (irBinaryMathNode.getBinaryType() == def.class ||
                     (irBinaryMathNode.getShiftType() != null && irBinaryMathNode.getShiftType() == def.class)) {
                     (irBinaryMathNode.getShiftType() != null && irBinaryMathNode.getShiftType() == def.class)) {
-                methodWriter.writeDynamicBinaryInstruction(irBinaryMathNode.getLocation(), irBinaryMathNode.getExpressionType(),
-                        irLeftNode.getExpressionType(), irRightNode.getExpressionType(), operation, irBinaryMathNode.getFlags());
+                methodWriter.writeDynamicBinaryInstruction(irBinaryMathNode.getLocation(),
+                        expressionType,
+                        irLeftNode.getDecoration(IRDExpressionType.class).getType(),
+                        irRightNode.getDecoration(IRDExpressionType.class).getType(),
+                        operation, irBinaryMathNode.getFlags());
             } else {
             } else {
-                methodWriter.writeBinaryInstruction(irBinaryMathNode.getLocation(), irBinaryMathNode.getExpressionType(), operation);
+                methodWriter.writeBinaryInstruction(irBinaryMathNode.getLocation(), expressionType, operation);
             }
             }
         }
         }
     }
     }
@@ -794,7 +801,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
 
 
         for (ExpressionNode argumentNode : irStringConcatenationNode.getArgumentNodes()) {
         for (ExpressionNode argumentNode : irStringConcatenationNode.getArgumentNodes()) {
             visit(argumentNode, writeScope);
             visit(argumentNode, writeScope);
-            methodWriter.writeAppendStrings(argumentNode.getExpressionType());
+            methodWriter.writeAppendStrings(argumentNode.getDecoration(IRDExpressionType.class).getType());
         }
         }
 
 
         methodWriter.writeToStrings();
         methodWriter.writeToStrings();
@@ -841,7 +848,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
             methodWriter.mark(end);
             methodWriter.mark(end);
         } else {
         } else {
             throw new IllegalStateException("unexpected boolean operation [" + operation + "] " +
             throw new IllegalStateException("unexpected boolean operation [" + operation + "] " +
-                    "for type [" + irBooleanNode.getExpressionCanonicalTypeName() + "]");
+                    "for type [" + irBooleanNode.getDecoration(IRDExpressionType.class).getCanonicalTypeName() + "]");
         }
         }
     }
     }
 
 
@@ -878,13 +885,13 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         if (comparisonType == void.class || comparisonType == byte.class
         if (comparisonType == void.class || comparisonType == byte.class
                 || comparisonType == short.class || comparisonType == char.class) {
                 || comparisonType == short.class || comparisonType == char.class) {
             throw new IllegalStateException("unexpected comparison operation [" + operation + "] " +
             throw new IllegalStateException("unexpected comparison operation [" + operation + "] " +
-                    "for type [" + irComparisonNode.getExpressionCanonicalTypeName() + "]");
+                    "for type [" + irComparisonNode.getDecoration(IRDExpressionType.class).getCanonicalTypeName() + "]");
         } else if (comparisonType == boolean.class) {
         } else if (comparisonType == boolean.class) {
             if (eq) methodWriter.ifCmp(type, MethodWriter.EQ, jump);
             if (eq) methodWriter.ifCmp(type, MethodWriter.EQ, jump);
             else if (ne) methodWriter.ifCmp(type, MethodWriter.NE, jump);
             else if (ne) methodWriter.ifCmp(type, MethodWriter.NE, jump);
             else {
             else {
                 throw new IllegalStateException("unexpected comparison operation [" + operation + "] " +
                 throw new IllegalStateException("unexpected comparison operation [" + operation + "] " +
-                        "for type [" + irComparisonNode.getExpressionCanonicalTypeName() + "]");
+                        "for type [" + irComparisonNode.getDecoration(IRDExpressionType.class).getCanonicalTypeName() + "]");
             }
             }
         } else if (comparisonType == int.class || comparisonType == long.class
         } else if (comparisonType == int.class || comparisonType == long.class
                 || comparisonType == float.class || comparisonType == double.class) {
                 || comparisonType == float.class || comparisonType == double.class) {
@@ -896,13 +903,14 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
             else if (gte) methodWriter.ifCmp(type, MethodWriter.GE, jump);
             else if (gte) methodWriter.ifCmp(type, MethodWriter.GE, jump);
             else {
             else {
                 throw new IllegalStateException("unexpected comparison operation [" + operation + "] " +
                 throw new IllegalStateException("unexpected comparison operation [" + operation + "] " +
-                        "for type [" + irComparisonNode.getExpressionCanonicalTypeName() + "]");
+                        "for type [" + irComparisonNode.getDecoration(IRDExpressionType.class).getCanonicalTypeName() + "]");
             }
             }
 
 
         } else if (comparisonType == def.class) {
         } else if (comparisonType == def.class) {
             Type booleanType = Type.getType(boolean.class);
             Type booleanType = Type.getType(boolean.class);
             Type descriptor = Type.getMethodType(booleanType,
             Type descriptor = Type.getMethodType(booleanType,
-                    MethodWriter.getType(irLeftNode.getExpressionType()), MethodWriter.getType(irRightNode.getExpressionType()));
+                    MethodWriter.getType(irLeftNode.getDecoration(IRDExpressionType.class).getType()),
+                    MethodWriter.getType(irRightNode.getDecoration(IRDExpressionType.class).getType()));
 
 
             if (eq) {
             if (eq) {
                 if (irRightNode instanceof NullNode) {
                 if (irRightNode instanceof NullNode) {
@@ -936,7 +944,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
                 writejump = false;
                 writejump = false;
             } else {
             } else {
                 throw new IllegalStateException("unexpected comparison operation [" + operation + "] " +
                 throw new IllegalStateException("unexpected comparison operation [" + operation + "] " +
-                        "for type [" + irComparisonNode.getExpressionCanonicalTypeName() + "]");
+                        "for type [" + irComparisonNode.getDecoration(IRDExpressionType.class).getCanonicalTypeName() + "]");
             }
             }
         } else {
         } else {
             if (eq) {
             if (eq) {
@@ -959,7 +967,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
                 }
                 }
             } else {
             } else {
                 throw new IllegalStateException("unexpected comparison operation [" + operation + "] " +
                 throw new IllegalStateException("unexpected comparison operation [" + operation + "] " +
-                        "for type [" + irComparisonNode.getExpressionCanonicalTypeName() + "]");
+                        "for type [" + irComparisonNode.getDecoration(IRDExpressionType.class).getCanonicalTypeName() + "]");
             }
             }
         }
         }
 
 
@@ -990,15 +998,16 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         visit(irChildNode, writeScope);
         visit(irChildNode, writeScope);
 
 
         Class<?> instanceType = irInstanceofNode.getInstanceType();
         Class<?> instanceType = irInstanceofNode.getInstanceType();
-        Class<?> expressionType = irInstanceofNode.getExpressionType();
+        Class<?> expressionType = irInstanceofNode.getDecoration(IRDExpressionType.class).getType();
 
 
         if (irInstanceofNode.getInstanceType() == def.class) {
         if (irInstanceofNode.getInstanceType() == def.class) {
             methodWriter.writePop(MethodWriter.getType(expressionType).getSize());
             methodWriter.writePop(MethodWriter.getType(expressionType).getSize());
             methodWriter.push(true);
             methodWriter.push(true);
-        } else if (irChildNode.getExpressionType().isPrimitive()) {
+        } else if (irChildNode.getDecoration(IRDExpressionType.class).getType().isPrimitive()) {
             methodWriter.writePop(MethodWriter.getType(expressionType).getSize());
             methodWriter.writePop(MethodWriter.getType(expressionType).getSize());
             Class<?> boxedInstanceType = PainlessLookupUtility.typeToBoxedType(instanceType);
             Class<?> boxedInstanceType = PainlessLookupUtility.typeToBoxedType(instanceType);
-            Class<?> boxedExpressionType = PainlessLookupUtility.typeToBoxedType(irChildNode.getExpressionType());
+            Class<?> childExpressionType = irChildNode.getDecoration(IRDExpressionType.class).getType();
+            Class<?> boxedExpressionType = PainlessLookupUtility.typeToBoxedType(childExpressionType);
             methodWriter.push(boxedInstanceType.isAssignableFrom(boxedExpressionType));
             methodWriter.push(boxedInstanceType.isAssignableFrom(boxedExpressionType));
         } else {
         } else {
             methodWriter.instanceOf(MethodWriter.getType(PainlessLookupUtility.typeToBoxedType(instanceType)));
             methodWriter.instanceOf(MethodWriter.getType(PainlessLookupUtility.typeToBoxedType(instanceType)));
@@ -1044,7 +1053,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         methodWriter.writeDebugInfo(irListInitializationNode.getLocation());
         methodWriter.writeDebugInfo(irListInitializationNode.getLocation());
 
 
         PainlessConstructor painlessConstructor = irListInitializationNode.getConstructor();
         PainlessConstructor painlessConstructor = irListInitializationNode.getConstructor();
-        methodWriter.newInstance(MethodWriter.getType(irListInitializationNode.getExpressionType()));
+        methodWriter.newInstance(MethodWriter.getType(irListInitializationNode.getDecoration(IRDExpressionType.class).getType()));
         methodWriter.dup();
         methodWriter.dup();
         methodWriter.invokeConstructor(
         methodWriter.invokeConstructor(
                 Type.getType(painlessConstructor.javaConstructor.getDeclaringClass()),
                 Type.getType(painlessConstructor.javaConstructor.getDeclaringClass()),
@@ -1064,7 +1073,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         methodWriter.writeDebugInfo(irMapInitializationNode.getLocation());
         methodWriter.writeDebugInfo(irMapInitializationNode.getLocation());
 
 
         PainlessConstructor painlessConstructor = irMapInitializationNode.getConstructor();
         PainlessConstructor painlessConstructor = irMapInitializationNode.getConstructor();
-        methodWriter.newInstance(MethodWriter.getType(irMapInitializationNode.getExpressionType()));
+        methodWriter.newInstance(MethodWriter.getType(irMapInitializationNode.getDecoration(IRDExpressionType.class).getType()));
         methodWriter.dup();
         methodWriter.dup();
         methodWriter.invokeConstructor(
         methodWriter.invokeConstructor(
                 Type.getType(painlessConstructor.javaConstructor.getDeclaringClass()),
                 Type.getType(painlessConstructor.javaConstructor.getDeclaringClass()),
@@ -1085,7 +1094,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         methodWriter.writeDebugInfo(irNewArrayNode.getLocation());
         methodWriter.writeDebugInfo(irNewArrayNode.getLocation());
 
 
         List<ExpressionNode> irArgumentNodes = irNewArrayNode.getArgumentNodes();
         List<ExpressionNode> irArgumentNodes = irNewArrayNode.getArgumentNodes();
-        Class<?> expressionType = irNewArrayNode.getExpressionType();
+        Class<?> expressionType = irNewArrayNode.getDecoration(IRDExpressionType.class).getType();
 
 
         if (irNewArrayNode.getInitialize()) {
         if (irNewArrayNode.getInitialize()) {
             methodWriter.push(irNewArrayNode.getArgumentNodes().size());
             methodWriter.push(irNewArrayNode.getArgumentNodes().size());
@@ -1119,7 +1128,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         MethodWriter methodWriter = writeScope.getMethodWriter();
         MethodWriter methodWriter = writeScope.getMethodWriter();
         methodWriter.writeDebugInfo(irNewObjectNode.getLocation());
         methodWriter.writeDebugInfo(irNewObjectNode.getLocation());
 
 
-        methodWriter.newInstance(MethodWriter.getType(irNewObjectNode.getExpressionType()));
+        methodWriter.newInstance(MethodWriter.getType(irNewObjectNode.getDecoration(IRDExpressionType.class).getType()));
 
 
         if (irNewObjectNode.getRead()) {
         if (irNewObjectNode.getRead()) {
             methodWriter.dup();
             methodWriter.dup();
@@ -1193,13 +1202,13 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         MethodWriter methodWriter = writeScope.getMethodWriter();
         MethodWriter methodWriter = writeScope.getMethodWriter();
         methodWriter.writeDebugInfo(irTypedCaptureReferenceNode.getLocation());
         methodWriter.writeDebugInfo(irTypedCaptureReferenceNode.getLocation());
         Variable captured = writeScope.getVariable(irTypedCaptureReferenceNode.getCaptures().get(0));
         Variable captured = writeScope.getVariable(irTypedCaptureReferenceNode.getCaptures().get(0));
+        Class<?> expressionType = irTypedCaptureReferenceNode.getDecoration(IRDExpressionType.class).getType();
+        String expressionCanonicalTypeName = irTypedCaptureReferenceNode.getDecoration(IRDExpressionType.class).getCanonicalTypeName();
 
 
         methodWriter.visitVarInsn(captured.getAsmType().getOpcode(Opcodes.ILOAD), captured.getSlot());
         methodWriter.visitVarInsn(captured.getAsmType().getOpcode(Opcodes.ILOAD), captured.getSlot());
-        Type methodType = Type.getMethodType(
-                MethodWriter.getType(irTypedCaptureReferenceNode.getExpressionType()),
-                captured.getAsmType());
-        methodWriter.invokeDefCall(irTypedCaptureReferenceNode.getMethodName(),
-                methodType, DefBootstrap.REFERENCE, irTypedCaptureReferenceNode.getExpressionCanonicalTypeName());
+        Type methodType = Type.getMethodType(MethodWriter.getType(expressionType), captured.getAsmType());
+        methodWriter.invokeDefCall(
+                irTypedCaptureReferenceNode.getMethodName(), methodType, DefBootstrap.REFERENCE, expressionCanonicalTypeName);
     }
     }
 
 
     @Override
     @Override
@@ -1238,7 +1247,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         MethodWriter methodWriter = writeScope.getMethodWriter();
         MethodWriter methodWriter = writeScope.getMethodWriter();
         methodWriter.writeDebugInfo(irLoadDotDefNode.getLocation());
         methodWriter.writeDebugInfo(irLoadDotDefNode.getLocation());
         Type methodType = Type.getMethodType(
         Type methodType = Type.getMethodType(
-                MethodWriter.getType(irLoadDotDefNode.getExpressionType()),
+                MethodWriter.getType(irLoadDotDefNode.getDecoration(IRDExpressionType.class).getType()),
                 MethodWriter.getType(def.class));
                 MethodWriter.getType(def.class));
         methodWriter.invokeDefCall(irLoadDotDefNode.getValue(), methodType, DefBootstrap.LOAD);
         methodWriter.invokeDefCall(irLoadDotDefNode.getValue(), methodType, DefBootstrap.LOAD);
     }
     }
@@ -1307,7 +1316,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
 
 
         boolean isStatic = irLoadFieldMemberNode.isStatic();
         boolean isStatic = irLoadFieldMemberNode.isStatic();
         String memberFieldName = irLoadFieldMemberNode.getName();
         String memberFieldName = irLoadFieldMemberNode.getName();
-        Type asmMemberFieldType = MethodWriter.getType(irLoadFieldMemberNode.getExpressionType());
+        Type asmMemberFieldType = MethodWriter.getType(irLoadFieldMemberNode.getDecoration(IRDExpressionType.class).getType());
 
 
         if (isStatic) {
         if (isStatic) {
             methodWriter.getStatic(CLASS_TYPE, memberFieldName, asmMemberFieldType);
             methodWriter.getStatic(CLASS_TYPE, memberFieldName, asmMemberFieldType);
@@ -1322,7 +1331,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         MethodWriter methodWriter = writeScope.getMethodWriter();
         MethodWriter methodWriter = writeScope.getMethodWriter();
         methodWriter.writeDebugInfo(irLoadBraceDefNode.getLocation());
         methodWriter.writeDebugInfo(irLoadBraceDefNode.getLocation());
         Type methodType = Type.getMethodType(
         Type methodType = Type.getMethodType(
-                MethodWriter.getType(irLoadBraceDefNode.getExpressionType()),
+                MethodWriter.getType(irLoadBraceDefNode.getDecoration(IRDExpressionType.class).getType()),
                 MethodWriter.getType(def.class),
                 MethodWriter.getType(def.class),
                 MethodWriter.getType(irLoadBraceDefNode.getIndexType()));
                 MethodWriter.getType(irLoadBraceDefNode.getIndexType()));
         methodWriter.invokeDefCall("arrayLoad", methodType, DefBootstrap.ARRAY_LOAD);
         methodWriter.invokeDefCall("arrayLoad", methodType, DefBootstrap.ARRAY_LOAD);
@@ -1332,7 +1341,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
     public void visitLoadBrace(LoadBraceNode irLoadBraceNode, WriteScope writeScope) {
     public void visitLoadBrace(LoadBraceNode irLoadBraceNode, WriteScope writeScope) {
         MethodWriter methodWriter = writeScope.getMethodWriter();
         MethodWriter methodWriter = writeScope.getMethodWriter();
         methodWriter.writeDebugInfo(irLoadBraceNode.getLocation());
         methodWriter.writeDebugInfo(irLoadBraceNode.getLocation());
-        methodWriter.arrayLoad(MethodWriter.getType(irLoadBraceNode.getExpressionType()));
+        methodWriter.arrayLoad(MethodWriter.getType(irLoadBraceNode.getDecoration(IRDExpressionType.class).getType()));
     }
     }
 
 
     @Override
     @Override
@@ -1484,7 +1493,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
             ExpressionNode irArgumentNode = irInvokeCallDefNode.getArgumentNodes().get(i);
             ExpressionNode irArgumentNode = irInvokeCallDefNode.getArgumentNodes().get(i);
             visit(irArgumentNode, writeScope);
             visit(irArgumentNode, writeScope);
 
 
-            typeParameters.add(irArgumentNode.getExpressionType());
+            typeParameters.add(irArgumentNode.getDecoration(IRDExpressionType.class).getType());
 
 
             // handle the case for unknown functional interface
             // handle the case for unknown functional interface
             // to hint at which values are the call's arguments
             // to hint at which values are the call's arguments
@@ -1514,7 +1523,8 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
             asmParameterTypes[index] = MethodWriter.getType(typeParameters.get(index));
             asmParameterTypes[index] = MethodWriter.getType(typeParameters.get(index));
         }
         }
 
 
-        Type methodType = Type.getMethodType(MethodWriter.getType(irInvokeCallDefNode.getExpressionType()), asmParameterTypes);
+        Type methodType = Type.getMethodType(MethodWriter.getType(
+                irInvokeCallDefNode.getDecoration(IRDExpressionType.class).getType()), asmParameterTypes);
 
 
         boostrapArguments.add(0, defCallRecipe.toString());
         boostrapArguments.add(0, defCallRecipe.toString());
         methodWriter.invokeDefCall(irInvokeCallDefNode.getName(), methodType, DefBootstrap.METHOD_CALL, boostrapArguments.toArray());
         methodWriter.invokeDefCall(irInvokeCallDefNode.getName(), methodType, DefBootstrap.METHOD_CALL, boostrapArguments.toArray());
@@ -1660,7 +1670,7 @@ public class DefaultIRTreeToASMBytesPhase implements IRTreeVisitor<WriteScope> {
         methodWriter.dup();
         methodWriter.dup();
         visit(irFlipDefIndexNode.getChildNode(), writeScope);
         visit(irFlipDefIndexNode.getChildNode(), writeScope);
 
 
-        Type asmExpressionType = MethodWriter.getType(irFlipDefIndexNode.getChildNode().getExpressionType());
+        Type asmExpressionType = MethodWriter.getType(irFlipDefIndexNode.getChildNode().getDecoration(IRDExpressionType.class).getType());
         Type asmDefType = MethodWriter.getType(def.class);
         Type asmDefType = MethodWriter.getType(def.class);
         Type methodType = Type.getMethodType(asmExpressionType, asmDefType, asmExpressionType);
         Type methodType = Type.getMethodType(asmExpressionType, asmDefType, asmExpressionType);
         methodWriter.invokeDefCall("normalizeIndex", methodType, DefBootstrap.INDEX_NORMALIZE);
         methodWriter.invokeDefCall("normalizeIndex", methodType, DefBootstrap.INDEX_NORMALIZE);

+ 152 - 120
modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/DefaultUserTreeToIRTreePhase.java

@@ -20,6 +20,7 @@
 package org.elasticsearch.painless.phase;
 package org.elasticsearch.painless.phase;
 
 
 import org.elasticsearch.painless.DefBootstrap;
 import org.elasticsearch.painless.DefBootstrap;
+import org.elasticsearch.painless.FunctionRef;
 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;
@@ -100,6 +101,7 @@ import org.elasticsearch.painless.ir.UnaryMathNode;
 import org.elasticsearch.painless.ir.WhileLoopNode;
 import org.elasticsearch.painless.ir.WhileLoopNode;
 import org.elasticsearch.painless.lookup.PainlessCast;
 import org.elasticsearch.painless.lookup.PainlessCast;
 import org.elasticsearch.painless.lookup.PainlessClassBinding;
 import org.elasticsearch.painless.lookup.PainlessClassBinding;
+import org.elasticsearch.painless.lookup.PainlessConstructor;
 import org.elasticsearch.painless.lookup.PainlessField;
 import org.elasticsearch.painless.lookup.PainlessField;
 import org.elasticsearch.painless.lookup.PainlessInstanceBinding;
 import org.elasticsearch.painless.lookup.PainlessInstanceBinding;
 import org.elasticsearch.painless.lookup.PainlessLookup;
 import org.elasticsearch.painless.lookup.PainlessLookup;
@@ -199,6 +201,7 @@ import org.elasticsearch.painless.symbol.Decorations.ValueType;
 import org.elasticsearch.painless.symbol.Decorations.Write;
 import org.elasticsearch.painless.symbol.Decorations.Write;
 import org.elasticsearch.painless.symbol.FunctionTable;
 import org.elasticsearch.painless.symbol.FunctionTable;
 import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction;
 import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction;
+import org.elasticsearch.painless.symbol.IRDecorations.IRDExpressionType;
 import org.elasticsearch.painless.symbol.ScriptScope;
 import org.elasticsearch.painless.symbol.ScriptScope;
 import org.elasticsearch.painless.symbol.SemanticScope.Variable;
 import org.elasticsearch.painless.symbol.SemanticScope.Variable;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Opcodes;
@@ -277,17 +280,17 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             blockNode.addStatementNode(returnNode);
             blockNode.addStatementNode(returnNode);
 
 
             BinaryImplNode irBinaryImplNode = new BinaryImplNode(internalLocation);
             BinaryImplNode irBinaryImplNode = new BinaryImplNode(internalLocation);
-            irBinaryImplNode.setExpressionType(CallSite.class);
+            irBinaryImplNode.attachDecoration(new IRDExpressionType(CallSite.class));
 
 
             returnNode.setExpressionNode(irBinaryImplNode);
             returnNode.setExpressionNode(irBinaryImplNode);
 
 
             StaticNode staticNode = new StaticNode(internalLocation);
             StaticNode staticNode = new StaticNode(internalLocation);
-            staticNode.setExpressionType(DefBootstrap.class);
+            staticNode.attachDecoration(new IRDExpressionType(DefBootstrap.class));
 
 
             irBinaryImplNode.setLeftNode(staticNode);
             irBinaryImplNode.setLeftNode(staticNode);
 
 
             InvokeCallNode invokeCallNode = new InvokeCallNode(internalLocation);
             InvokeCallNode invokeCallNode = new InvokeCallNode(internalLocation);
-            invokeCallNode.setExpressionType(CallSite.class);
+            invokeCallNode.attachDecoration(new IRDExpressionType(CallSite.class));
             invokeCallNode.setMethod(new PainlessMethod(
             invokeCallNode.setMethod(new PainlessMethod(
                             DefBootstrap.class.getMethod("bootstrap",
                             DefBootstrap.class.getMethod("bootstrap",
                                     PainlessLookup.class,
                                     PainlessLookup.class,
@@ -321,58 +324,58 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             irBinaryImplNode.setRightNode(invokeCallNode);
             irBinaryImplNode.setRightNode(invokeCallNode);
 
 
             LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
             LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
-            irLoadFieldMemberNode.setExpressionType(PainlessLookup.class);
+            irLoadFieldMemberNode.attachDecoration(new IRDExpressionType(PainlessLookup.class));
             irLoadFieldMemberNode.setName("$DEFINITION");
             irLoadFieldMemberNode.setName("$DEFINITION");
             irLoadFieldMemberNode.setStatic(true);
             irLoadFieldMemberNode.setStatic(true);
 
 
             invokeCallNode.addArgumentNode(irLoadFieldMemberNode);
             invokeCallNode.addArgumentNode(irLoadFieldMemberNode);
 
 
             irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
             irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
-            irLoadFieldMemberNode.setExpressionType(FunctionTable.class);
+            irLoadFieldMemberNode.attachDecoration(new IRDExpressionType(FunctionTable.class));
             irLoadFieldMemberNode.setName("$FUNCTIONS");
             irLoadFieldMemberNode.setName("$FUNCTIONS");
             irLoadFieldMemberNode.setStatic(true);
             irLoadFieldMemberNode.setStatic(true);
 
 
             invokeCallNode.addArgumentNode(irLoadFieldMemberNode);
             invokeCallNode.addArgumentNode(irLoadFieldMemberNode);
 
 
             irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
             irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
-            irLoadFieldMemberNode.setExpressionType(Map.class);
+            irLoadFieldMemberNode.attachDecoration(new IRDExpressionType(Map.class));
             irLoadFieldMemberNode.setName("$COMPILERSETTINGS");
             irLoadFieldMemberNode.setName("$COMPILERSETTINGS");
             irLoadFieldMemberNode.setStatic(true);
             irLoadFieldMemberNode.setStatic(true);
 
 
             invokeCallNode.addArgumentNode(irLoadFieldMemberNode);
             invokeCallNode.addArgumentNode(irLoadFieldMemberNode);
 
 
             LoadVariableNode irLoadVariableNode = new LoadVariableNode(internalLocation);
             LoadVariableNode irLoadVariableNode = new LoadVariableNode(internalLocation);
-            irLoadVariableNode.setExpressionType(Lookup.class);
+            irLoadVariableNode.attachDecoration(new IRDExpressionType(Lookup.class));
             irLoadVariableNode.setName("methodHandlesLookup");
             irLoadVariableNode.setName("methodHandlesLookup");
 
 
             invokeCallNode.addArgumentNode(irLoadVariableNode);
             invokeCallNode.addArgumentNode(irLoadVariableNode);
 
 
             irLoadVariableNode = new LoadVariableNode(internalLocation);
             irLoadVariableNode = new LoadVariableNode(internalLocation);
-            irLoadVariableNode.setExpressionType(String.class);
+            irLoadVariableNode.attachDecoration(new IRDExpressionType(String.class));
             irLoadVariableNode.setName("name");
             irLoadVariableNode.setName("name");
 
 
             invokeCallNode.addArgumentNode(irLoadVariableNode);
             invokeCallNode.addArgumentNode(irLoadVariableNode);
 
 
             irLoadVariableNode = new LoadVariableNode(internalLocation);
             irLoadVariableNode = new LoadVariableNode(internalLocation);
-            irLoadVariableNode.setExpressionType(MethodType.class);
+            irLoadVariableNode.attachDecoration(new IRDExpressionType(MethodType.class));
             irLoadVariableNode.setName("type");
             irLoadVariableNode.setName("type");
 
 
             invokeCallNode.addArgumentNode(irLoadVariableNode);
             invokeCallNode.addArgumentNode(irLoadVariableNode);
 
 
             irLoadVariableNode = new LoadVariableNode(internalLocation);
             irLoadVariableNode = new LoadVariableNode(internalLocation);
-            irLoadVariableNode.setExpressionType(int.class);
+            irLoadVariableNode.attachDecoration(new IRDExpressionType(int.class));
             irLoadVariableNode.setName("initialDepth");
             irLoadVariableNode.setName("initialDepth");
 
 
             invokeCallNode.addArgumentNode(irLoadVariableNode);
             invokeCallNode.addArgumentNode(irLoadVariableNode);
 
 
             irLoadVariableNode = new LoadVariableNode(internalLocation);
             irLoadVariableNode = new LoadVariableNode(internalLocation);
-            irLoadVariableNode.setExpressionType(int.class);
+            irLoadVariableNode.attachDecoration(new IRDExpressionType(int.class));
             irLoadVariableNode.setName("flavor");
             irLoadVariableNode.setName("flavor");
 
 
             invokeCallNode.addArgumentNode(irLoadVariableNode);
             invokeCallNode.addArgumentNode(irLoadVariableNode);
 
 
             irLoadVariableNode = new LoadVariableNode(internalLocation);
             irLoadVariableNode = new LoadVariableNode(internalLocation);
-            irLoadVariableNode.setExpressionType(Object[].class);
+            irLoadVariableNode.attachDecoration(new IRDExpressionType(Object[].class));
             irLoadVariableNode.setName("args");
             irLoadVariableNode.setName("args");
 
 
             invokeCallNode.addArgumentNode(irLoadVariableNode);
             invokeCallNode.addArgumentNode(irLoadVariableNode);
@@ -404,7 +407,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         }
         }
 
 
         CastNode irCastNode = new CastNode(irExpressionNode.getLocation());
         CastNode irCastNode = new CastNode(irExpressionNode.getLocation());
-        irCastNode.setExpressionType(targetType);
+        irCastNode.attachDecoration(new IRDExpressionType(targetType));
         irCastNode.setCast(painlessCast);
         irCastNode.setCast(painlessCast);
         irCastNode.setChildNode(irExpressionNode);
         irCastNode.setChildNode(irExpressionNode);
 
 
@@ -440,12 +443,12 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 if (isNullSafe) {
                 if (isNullSafe) {
                     // the null-safe structure is slightly different from the standard structure since
                     // 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
                     // both the index and expression are not written to the stack if the prefix is null
-                    binaryImplNode.setExpressionType(irExpressionNode.getExpressionType());
+                    binaryImplNode.copyDecorationFrom(irExpressionNode, IRDExpressionType.class);
                     binaryImplNode.setLeftNode(irIndexNode);
                     binaryImplNode.setLeftNode(irIndexNode);
                     binaryImplNode.setRightNode(irExpressionNode);
                     binaryImplNode.setRightNode(irExpressionNode);
                     irExpressionNode = binaryImplNode;
                     irExpressionNode = binaryImplNode;
                 } else {
                 } else {
-                    binaryImplNode.setExpressionType(void.class);
+                    binaryImplNode.attachDecoration(new IRDExpressionType(void.class));
                     binaryImplNode.setLeftNode(irPrefixNode);
                     binaryImplNode.setLeftNode(irPrefixNode);
                     binaryImplNode.setRightNode(irIndexNode);
                     binaryImplNode.setRightNode(irIndexNode);
                     irPrefixNode = binaryImplNode;
                     irPrefixNode = binaryImplNode;
@@ -455,7 +458,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             if (irLoadNode != null && irStoreNode != null) {
             if (irLoadNode != null && irStoreNode != null) {
                 // this is a compound assignment and requires and additional dup to re-access the prefix
                 // this is a compound assignment and requires and additional dup to re-access the prefix
                 DupNode dupNode = new DupNode(location);
                 DupNode dupNode = new DupNode(location);
-                dupNode.setExpressionType(void.class);
+                dupNode.attachDecoration(new IRDExpressionType(void.class));
                 dupNode.setSize(accessDepth);
                 dupNode.setSize(accessDepth);
                 dupNode.setDepth(0);
                 dupNode.setDepth(0);
                 dupNode.setChildNode(irPrefixNode);
                 dupNode.setChildNode(irPrefixNode);
@@ -464,12 +467,12 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
             // build the structure to combine the prefix and the load/store
             // build the structure to combine the prefix and the load/store
             BinaryImplNode binaryImplNode = new BinaryImplNode(location);
             BinaryImplNode binaryImplNode = new BinaryImplNode(location);
-            binaryImplNode.setExpressionType(irExpressionNode.getExpressionType());
+            binaryImplNode.copyDecorationFrom(irExpressionNode, IRDExpressionType.class);
 
 
             if (isNullSafe) {
             if (isNullSafe) {
                 // build the structure for a null safe load
                 // build the structure for a null safe load
                 NullSafeSubNode irNullSafeSubNode = new NullSafeSubNode(location);
                 NullSafeSubNode irNullSafeSubNode = new NullSafeSubNode(location);
-                irNullSafeSubNode.setExpressionType(irExpressionNode.getExpressionType());
+                irNullSafeSubNode.copyDecorationFrom(irExpressionNode, IRDExpressionType.class);
                 irNullSafeSubNode.setChildNode(irExpressionNode);
                 irNullSafeSubNode.setChildNode(irExpressionNode);
                 binaryImplNode.setLeftNode(irPrefixNode);
                 binaryImplNode.setLeftNode(irPrefixNode);
                 binaryImplNode.setRightNode(irNullSafeSubNode);
                 binaryImplNode.setRightNode(irNullSafeSubNode);
@@ -532,7 +535,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             } else if (userFunctionNode.isAutoReturnEnabled()) {
             } else if (userFunctionNode.isAutoReturnEnabled()) {
                 if (returnType.isPrimitive()) {
                 if (returnType.isPrimitive()) {
                     ConstantNode irConstantNode = new ConstantNode(userFunctionNode.getLocation());
                     ConstantNode irConstantNode = new ConstantNode(userFunctionNode.getLocation());
-                    irConstantNode.setExpressionType(returnType);
+                    irConstantNode.attachDecoration(new IRDExpressionType(returnType));
 
 
                     if (returnType == boolean.class) {
                     if (returnType == boolean.class) {
                         irConstantNode.setConstant(false);
                         irConstantNode.setConstant(false);
@@ -554,7 +557,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                     irExpressionNode = irConstantNode;
                     irExpressionNode = irConstantNode;
                 } else {
                 } else {
                     irExpressionNode = new NullNode(userFunctionNode.getLocation());
                     irExpressionNode = new NullNode(userFunctionNode.getLocation());
-                    irExpressionNode.setExpressionType(returnType);
+                    irExpressionNode.attachDecoration(new IRDExpressionType(returnType));
                 }
                 }
             } else {
             } else {
                 throw userFunctionNode.createError(new IllegalStateException("illegal tree structure"));
                 throw userFunctionNode.createError(new IllegalStateException("illegal tree structure"));
@@ -809,7 +812,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             // handles when the operation is a string concatenation
             // handles when the operation is a string concatenation
             if (concatenate) {
             if (concatenate) {
                 StringConcatenationNode stringConcatenationNode = new StringConcatenationNode(irStoreNode.getLocation());
                 StringConcatenationNode stringConcatenationNode = new StringConcatenationNode(irStoreNode.getLocation());
-                stringConcatenationNode.setExpressionType(String.class);
+                stringConcatenationNode.attachDecoration(new IRDExpressionType(String.class));
                 irCompoundNode = stringConcatenationNode;
                 irCompoundNode = stringConcatenationNode;
 
 
                 // must handle the StringBuilder case for java version <= 8
                 // must handle the StringBuilder case for java version <= 8
@@ -820,7 +823,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             } else {
             } else {
                 BinaryMathNode irBinaryMathNode = new BinaryMathNode(irStoreNode.getLocation());
                 BinaryMathNode irBinaryMathNode = new BinaryMathNode(irStoreNode.getLocation());
                 irBinaryMathNode.setLeftNode(irLoadNode);
                 irBinaryMathNode.setLeftNode(irLoadNode);
-                irBinaryMathNode.setExpressionType(compoundType);
+                irBinaryMathNode.attachDecoration(new IRDExpressionType(compoundType));
                 irBinaryMathNode.setBinaryType(compoundType);
                 irBinaryMathNode.setBinaryType(compoundType);
                 irBinaryMathNode.setOperation(userAssignmentNode.getOperation());
                 irBinaryMathNode.setOperation(userAssignmentNode.getOperation());
                 // add a compound assignment flag to the binary math node
                 // add a compound assignment flag to the binary math node
@@ -833,12 +836,12 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
             // no need to downcast so the binary math node is the value for the store node
             // no need to downcast so the binary math node is the value for the store node
             if (downcast == null) {
             if (downcast == null) {
-                irCompoundNode.setExpressionType(irStoreNode.getStoreType());
+                irCompoundNode.attachDecoration(new IRDExpressionType(irStoreNode.getStoreType()));
                 irStoreNode.setChildNode(irCompoundNode);
                 irStoreNode.setChildNode(irCompoundNode);
             // add a cast node to do a downcast as the value for the store node
             // add a cast node to do a downcast as the value for the store node
             } else {
             } else {
                 CastNode irCastNode = new CastNode(irCompoundNode.getLocation());
                 CastNode irCastNode = new CastNode(irCompoundNode.getLocation());
-                irCastNode.setExpressionType(downcast.targetType);
+                irCastNode.attachDecoration(new IRDExpressionType(downcast.targetType));
                 irCastNode.setCast(downcast);
                 irCastNode.setCast(downcast);
                 irCastNode.setChildNode(irCompoundNode);
                 irCastNode.setChildNode(irCompoundNode);
                 irStoreNode.setChildNode(irCastNode);
                 irStoreNode.setChildNode(irCastNode);
@@ -852,16 +855,16 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 // the value is read from prior to assignment (post-increment)
                 // the value is read from prior to assignment (post-increment)
                 if (userAssignmentNode.postIfRead()) {
                 if (userAssignmentNode.postIfRead()) {
                     irDupNode = new DupNode(irLoadNode.getLocation());
                     irDupNode = new DupNode(irLoadNode.getLocation());
-                    irDupNode.setExpressionType(irLoadNode.getExpressionType());
-                    irDupNode.setSize(MethodWriter.getType(irLoadNode.getExpressionType()).getSize());
+                    irDupNode.copyDecorationFrom(irLoadNode, IRDExpressionType.class);
+                    irDupNode.setSize(MethodWriter.getType(irLoadNode.getDecoration(IRDExpressionType.class).getType()).getSize());
                     irDupNode.setDepth(accessDepth);
                     irDupNode.setDepth(accessDepth);
                     irDupNode.setChildNode(irLoadNode);
                     irDupNode.setChildNode(irLoadNode);
                     irLoadNode = irDupNode;
                     irLoadNode = irDupNode;
                 // the value is read from after the assignment (pre-increment/compound)
                 // the value is read from after the assignment (pre-increment/compound)
                 } else {
                 } else {
                     irDupNode = new DupNode(irStoreNode.getLocation());
                     irDupNode = new DupNode(irStoreNode.getLocation());
-                    irDupNode.setExpressionType(irStoreNode.getStoreType());
-                    irDupNode.setSize(MethodWriter.getType(irStoreNode.getExpressionType()).getSize());
+                    irDupNode.attachDecoration(new IRDExpressionType(irStoreNode.getStoreType()));
+                    irDupNode.setSize(MethodWriter.getType(irStoreNode.getDecoration(IRDExpressionType.class).getType()).getSize());
                     irDupNode.setDepth(accessDepth);
                     irDupNode.setDepth(accessDepth);
                     irDupNode.setChildNode(irStoreNode.getChildNode());
                     irDupNode.setChildNode(irStoreNode.getChildNode());
                     irStoreNode.setChildNode(irDupNode);
                     irStoreNode.setChildNode(irDupNode);
@@ -874,7 +877,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             // upcast the stored value if necessary
             // upcast the stored value if necessary
             if (upcast != null) {
             if (upcast != null) {
                 CastNode irCastNode = new CastNode(irLoadNode.getLocation());
                 CastNode irCastNode = new CastNode(irLoadNode.getLocation());
-                irCastNode.setExpressionType(upcast.targetType);
+                irCastNode.attachDecoration(new IRDExpressionType(upcast.targetType));
                 irCastNode.setCast(upcast);
                 irCastNode.setCast(upcast);
                 irCastNode.setChildNode(irLoadNode);
                 irCastNode.setChildNode(irLoadNode);
                 irLoadNode = irCastNode;
                 irLoadNode = irCastNode;
@@ -900,8 +903,8 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 int accessDepth = scriptScope.getDecoration(userAssignmentNode.getLeftNode(), AccessDepth.class).getAccessDepth();
                 int accessDepth = scriptScope.getDecoration(userAssignmentNode.getLeftNode(), AccessDepth.class).getAccessDepth();
 
 
                 DupNode irDupNode = new DupNode(irValueNode.getLocation());
                 DupNode irDupNode = new DupNode(irValueNode.getLocation());
-                irDupNode.setExpressionType(irValueNode.getExpressionType());
-                irDupNode.setSize(MethodWriter.getType(irValueNode.getExpressionType()).getSize());
+                irDupNode.copyDecorationFrom(irValueNode, IRDExpressionType.class);
+                irDupNode.setSize(MethodWriter.getType(irValueNode.getDecoration(IRDExpressionType.class).getType()).getSize());
                 irDupNode.setDepth(accessDepth);
                 irDupNode.setDepth(accessDepth);
                 irDupNode.setChildNode(irValueNode);
                 irDupNode.setChildNode(irValueNode);
                 irValueNode = irDupNode;
                 irValueNode = irDupNode;
@@ -928,7 +931,8 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             irNode = visit(userUnaryNode.getChildNode(), scriptScope);
             irNode = visit(userUnaryNode.getChildNode(), scriptScope);
         } else {
         } else {
             UnaryMathNode irUnaryMathNode = new UnaryMathNode(userUnaryNode.getLocation());
             UnaryMathNode irUnaryMathNode = new UnaryMathNode(userUnaryNode.getLocation());
-            irUnaryMathNode.setExpressionType(scriptScope.getDecoration(userUnaryNode, ValueType.class).getValueType());
+            irUnaryMathNode.attachDecoration(
+                    new IRDExpressionType(scriptScope.getDecoration(userUnaryNode, ValueType.class).getValueType()));
             irUnaryMathNode.setUnaryType(unaryType);
             irUnaryMathNode.setUnaryType(unaryType);
             irUnaryMathNode.setOperation(userUnaryNode.getOperation());
             irUnaryMathNode.setOperation(userUnaryNode.getOperation());
             irUnaryMathNode.setOriginallyExplicit(scriptScope.getCondition(userUnaryNode, Explicit.class));
             irUnaryMathNode.setOriginallyExplicit(scriptScope.getCondition(userUnaryNode, Explicit.class));
@@ -973,14 +977,16 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             irExpressionNode = irBinaryMathNode;
             irExpressionNode = irBinaryMathNode;
         }
         }
 
 
-        irExpressionNode.setExpressionType(valueType);
+        irExpressionNode.attachDecoration(new IRDExpressionType(valueType));
         scriptScope.putDecoration(userBinaryNode, new IRNodeDecoration(irExpressionNode));
         scriptScope.putDecoration(userBinaryNode, new IRNodeDecoration(irExpressionNode));
     }
     }
 
 
     @Override
     @Override
     public void visitBooleanComp(EBooleanComp userBooleanCompNode, ScriptScope scriptScope) {
     public void visitBooleanComp(EBooleanComp userBooleanCompNode, ScriptScope scriptScope) {
+        Class<?> valueType = scriptScope.getDecoration(userBooleanCompNode, ValueType.class).getValueType();
+
         BooleanNode irBooleanNode = new BooleanNode(userBooleanCompNode.getLocation());
         BooleanNode irBooleanNode = new BooleanNode(userBooleanCompNode.getLocation());
-        irBooleanNode.setExpressionType(scriptScope.getDecoration(userBooleanCompNode, ValueType.class).getValueType());
+        irBooleanNode.attachDecoration(new IRDExpressionType(valueType));
         irBooleanNode.setOperation(userBooleanCompNode.getOperation());
         irBooleanNode.setOperation(userBooleanCompNode.getOperation());
         irBooleanNode.setLeftNode(injectCast(userBooleanCompNode.getLeftNode(), scriptScope));
         irBooleanNode.setLeftNode(injectCast(userBooleanCompNode.getLeftNode(), scriptScope));
         irBooleanNode.setRightNode(injectCast(userBooleanCompNode.getRightNode(), scriptScope));
         irBooleanNode.setRightNode(injectCast(userBooleanCompNode.getRightNode(), scriptScope));
@@ -991,7 +997,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
     @Override
     @Override
     public void visitComp(EComp userCompNode, ScriptScope scriptScope) {
     public void visitComp(EComp userCompNode, ScriptScope scriptScope) {
         ComparisonNode irComparisonNode = new ComparisonNode(userCompNode.getLocation());
         ComparisonNode irComparisonNode = new ComparisonNode(userCompNode.getLocation());
-        irComparisonNode.setExpressionType(scriptScope.getDecoration(userCompNode, ValueType.class).getValueType());
+        irComparisonNode.attachDecoration(new IRDExpressionType(scriptScope.getDecoration(userCompNode, ValueType.class).getValueType()));
         irComparisonNode.setComparisonType(scriptScope.getDecoration(userCompNode, ComparisonType.class).getComparisonType());
         irComparisonNode.setComparisonType(scriptScope.getDecoration(userCompNode, ComparisonType.class).getComparisonType());
         irComparisonNode.setOperation(userCompNode.getOperation());
         irComparisonNode.setOperation(userCompNode.getOperation());
         irComparisonNode.setLeftNode(injectCast(userCompNode.getLeftNode(), scriptScope));
         irComparisonNode.setLeftNode(injectCast(userCompNode.getLeftNode(), scriptScope));
@@ -1007,9 +1013,12 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
     @Override
     @Override
     public void visitInstanceof(EInstanceof userInstanceofNode, ScriptScope scriptScope) {
     public void visitInstanceof(EInstanceof userInstanceofNode, ScriptScope scriptScope) {
+        Class<?> valuetype = scriptScope.getDecoration(userInstanceofNode, ValueType.class).getValueType();
+        Class<?> instanceType = scriptScope.getDecoration(userInstanceofNode, InstanceType.class).getInstanceType();
+
         InstanceofNode irInstanceofNode = new InstanceofNode(userInstanceofNode.getLocation());
         InstanceofNode irInstanceofNode = new InstanceofNode(userInstanceofNode.getLocation());
-        irInstanceofNode.setExpressionType(scriptScope.getDecoration(userInstanceofNode, ValueType.class).getValueType());
-        irInstanceofNode.setInstanceType(scriptScope.getDecoration(userInstanceofNode, InstanceType.class).getInstanceType());
+        irInstanceofNode.attachDecoration(new IRDExpressionType(valuetype));
+        irInstanceofNode.setInstanceType(instanceType);
         irInstanceofNode.setChildNode((ExpressionNode)visit(userInstanceofNode.getExpressionNode(), scriptScope));
         irInstanceofNode.setChildNode((ExpressionNode)visit(userInstanceofNode.getExpressionNode(), scriptScope));
 
 
         scriptScope.putDecoration(userInstanceofNode, new IRNodeDecoration(irInstanceofNode));
         scriptScope.putDecoration(userInstanceofNode, new IRNodeDecoration(irInstanceofNode));
@@ -1018,7 +1027,8 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
     @Override
     @Override
     public void visitConditional(EConditional userConditionalNode, ScriptScope scriptScope) {
     public void visitConditional(EConditional userConditionalNode, ScriptScope scriptScope) {
         ConditionalNode irConditionalNode = new ConditionalNode(userConditionalNode.getLocation());
         ConditionalNode irConditionalNode = new ConditionalNode(userConditionalNode.getLocation());
-        irConditionalNode.setExpressionType(scriptScope.getDecoration(userConditionalNode, ValueType.class).getValueType());
+        irConditionalNode.attachDecoration(
+                new IRDExpressionType(scriptScope.getDecoration(userConditionalNode, ValueType.class).getValueType()));
         irConditionalNode.setConditionNode(injectCast(userConditionalNode.getConditionNode(), scriptScope));
         irConditionalNode.setConditionNode(injectCast(userConditionalNode.getConditionNode(), scriptScope));
         irConditionalNode.setLeftNode(injectCast(userConditionalNode.getTrueNode(), scriptScope));
         irConditionalNode.setLeftNode(injectCast(userConditionalNode.getTrueNode(), scriptScope));
         irConditionalNode.setRightNode(injectCast(userConditionalNode.getFalseNode(), scriptScope));
         irConditionalNode.setRightNode(injectCast(userConditionalNode.getFalseNode(), scriptScope));
@@ -1029,7 +1039,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
     @Override
     @Override
     public void visitElvis(EElvis userElvisNode, ScriptScope scriptScope) {
     public void visitElvis(EElvis userElvisNode, ScriptScope scriptScope) {
         ElvisNode irElvisNode = new ElvisNode(userElvisNode.getLocation());
         ElvisNode irElvisNode = new ElvisNode(userElvisNode.getLocation());
-        irElvisNode.setExpressionType(scriptScope.getDecoration(userElvisNode, ValueType.class).getValueType());
+        irElvisNode.attachDecoration(new IRDExpressionType(scriptScope.getDecoration(userElvisNode, ValueType.class).getValueType()));
         irElvisNode.setLeftNode(injectCast(userElvisNode.getLeftNode(), scriptScope));
         irElvisNode.setLeftNode(injectCast(userElvisNode.getLeftNode(), scriptScope));
         irElvisNode.setRightNode(injectCast(userElvisNode.getRightNode(), scriptScope));
         irElvisNode.setRightNode(injectCast(userElvisNode.getRightNode(), scriptScope));
 
 
@@ -1040,7 +1050,8 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
     public void visitListInit(EListInit userListInitNode, ScriptScope scriptScope) {
     public void visitListInit(EListInit userListInitNode, ScriptScope scriptScope) {
         ListInitializationNode irListInitializationNode = new ListInitializationNode(userListInitNode.getLocation());
         ListInitializationNode irListInitializationNode = new ListInitializationNode(userListInitNode.getLocation());
 
 
-        irListInitializationNode.setExpressionType(scriptScope.getDecoration(userListInitNode, ValueType.class).getValueType());
+        irListInitializationNode.attachDecoration(
+                new IRDExpressionType(scriptScope.getDecoration(userListInitNode, ValueType.class).getValueType()));
         irListInitializationNode.setConstructor(
         irListInitializationNode.setConstructor(
                 scriptScope.getDecoration(userListInitNode, StandardPainlessConstructor.class).getStandardPainlessConstructor());
                 scriptScope.getDecoration(userListInitNode, StandardPainlessConstructor.class).getStandardPainlessConstructor());
         irListInitializationNode.setMethod(
         irListInitializationNode.setMethod(
@@ -1057,7 +1068,8 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
     public void visitMapInit(EMapInit userMapInitNode, ScriptScope scriptScope) {
     public void visitMapInit(EMapInit userMapInitNode, ScriptScope scriptScope) {
         MapInitializationNode irMapInitializationNode = new MapInitializationNode(userMapInitNode.getLocation());
         MapInitializationNode irMapInitializationNode = new MapInitializationNode(userMapInitNode.getLocation());
 
 
-        irMapInitializationNode.setExpressionType(scriptScope.getDecoration(userMapInitNode, ValueType.class).getValueType());
+        irMapInitializationNode.attachDecoration(
+                new IRDExpressionType(scriptScope.getDecoration(userMapInitNode, ValueType.class).getValueType()));
         irMapInitializationNode.setConstructor(
         irMapInitializationNode.setConstructor(
                 scriptScope.getDecoration(userMapInitNode, StandardPainlessConstructor.class).getStandardPainlessConstructor());
                 scriptScope.getDecoration(userMapInitNode, StandardPainlessConstructor.class).getStandardPainlessConstructor());
         irMapInitializationNode.setMethod(
         irMapInitializationNode.setMethod(
@@ -1077,7 +1089,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
     public void visitNewArray(ENewArray userNewArrayNode, ScriptScope scriptScope) {
     public void visitNewArray(ENewArray userNewArrayNode, ScriptScope scriptScope) {
         NewArrayNode irNewArrayNode = new NewArrayNode(userNewArrayNode.getLocation());
         NewArrayNode irNewArrayNode = new NewArrayNode(userNewArrayNode.getLocation());
 
 
-        irNewArrayNode.setExpressionType(scriptScope.getDecoration(userNewArrayNode, ValueType.class).getValueType());
+        irNewArrayNode.attachDecoration(new IRDExpressionType(scriptScope.getDecoration(userNewArrayNode, ValueType.class).getValueType()));
         irNewArrayNode.setInitialize(userNewArrayNode.isInitializer());
         irNewArrayNode.setInitialize(userNewArrayNode.isInitializer());
 
 
         for (AExpression userArgumentNode : userNewArrayNode.getValueNodes()) {
         for (AExpression userArgumentNode : userNewArrayNode.getValueNodes()) {
@@ -1089,12 +1101,15 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
     @Override
     @Override
     public void visitNewObj(ENewObj userNewObjectNode, ScriptScope scriptScope) {
     public void visitNewObj(ENewObj userNewObjectNode, ScriptScope scriptScope) {
+        Class<?> valueType = scriptScope.getDecoration(userNewObjectNode, ValueType.class).getValueType();
+        PainlessConstructor painlessConstructor =
+                scriptScope.getDecoration(userNewObjectNode, StandardPainlessConstructor.class).getStandardPainlessConstructor();
+
         NewObjectNode irNewObjectNode = new NewObjectNode(userNewObjectNode.getLocation());
         NewObjectNode irNewObjectNode = new NewObjectNode(userNewObjectNode.getLocation());
 
 
-        irNewObjectNode.setExpressionType(scriptScope.getDecoration(userNewObjectNode, ValueType.class).getValueType());
+        irNewObjectNode.attachDecoration(new IRDExpressionType(valueType));
         irNewObjectNode.setRead(scriptScope.getCondition(userNewObjectNode, Read.class));
         irNewObjectNode.setRead(scriptScope.getCondition(userNewObjectNode, Read.class));
-        irNewObjectNode.setConstructor(
-                scriptScope.getDecoration(userNewObjectNode, StandardPainlessConstructor.class).getStandardPainlessConstructor());
+        irNewObjectNode.setConstructor(painlessConstructor);
 
 
         for (AExpression userArgumentNode : userNewObjectNode.getArgumentNodes()) {
         for (AExpression userArgumentNode : userNewObjectNode.getArgumentNodes()) {
             irNewObjectNode.addArgumentNode(injectCast(userArgumentNode, scriptScope));
             irNewObjectNode.addArgumentNode(injectCast(userArgumentNode, scriptScope));
@@ -1108,11 +1123,12 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         InvokeCallMemberNode irInvokeCallMemberNode = new InvokeCallMemberNode(callLocalNode.getLocation());
         InvokeCallMemberNode irInvokeCallMemberNode = new InvokeCallMemberNode(callLocalNode.getLocation());
 
 
         if (scriptScope.hasDecoration(callLocalNode, StandardLocalFunction.class)) {
         if (scriptScope.hasDecoration(callLocalNode, StandardLocalFunction.class)) {
-            irInvokeCallMemberNode.setLocalFunction(
-                    scriptScope.getDecoration(callLocalNode, StandardLocalFunction.class).getLocalFunction());
+            LocalFunction localFunction = scriptScope.getDecoration(callLocalNode, StandardLocalFunction.class).getLocalFunction();
+            irInvokeCallMemberNode.setLocalFunction(localFunction);
         } else if (scriptScope.hasDecoration(callLocalNode, StandardPainlessMethod.class)) {
         } else if (scriptScope.hasDecoration(callLocalNode, StandardPainlessMethod.class)) {
-            irInvokeCallMemberNode.setImportedMethod(
-                    scriptScope.getDecoration(callLocalNode, StandardPainlessMethod.class).getStandardPainlessMethod());
+            PainlessMethod importedMethod =
+                    scriptScope.getDecoration(callLocalNode, StandardPainlessMethod.class).getStandardPainlessMethod();
+            irInvokeCallMemberNode.setImportedMethod(importedMethod);
         } else if (scriptScope.hasDecoration(callLocalNode, StandardPainlessClassBinding.class)) {
         } else if (scriptScope.hasDecoration(callLocalNode, StandardPainlessClassBinding.class)) {
             PainlessClassBinding painlessClassBinding =
             PainlessClassBinding painlessClassBinding =
                     scriptScope.getDecoration(callLocalNode, StandardPainlessClassBinding.class).getPainlessClassBinding();
                     scriptScope.getDecoration(callLocalNode, StandardPainlessClassBinding.class).getPainlessClassBinding();
@@ -1151,43 +1167,56 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             irInvokeCallMemberNode.addArgumentNode(injectCast(userArgumentNode, scriptScope));
             irInvokeCallMemberNode.addArgumentNode(injectCast(userArgumentNode, scriptScope));
         }
         }
 
 
-        irInvokeCallMemberNode.setExpressionType(scriptScope.getDecoration(callLocalNode, ValueType.class).getValueType());
+        Class<?> valueType = scriptScope.getDecoration(callLocalNode, ValueType.class).getValueType();
+        irInvokeCallMemberNode.attachDecoration(new IRDExpressionType(valueType));
 
 
         scriptScope.putDecoration(callLocalNode, new IRNodeDecoration(irInvokeCallMemberNode));
         scriptScope.putDecoration(callLocalNode, new IRNodeDecoration(irInvokeCallMemberNode));
     }
     }
 
 
     @Override
     @Override
     public void visitBooleanConstant(EBooleanConstant userBooleanConstantNode, ScriptScope scriptScope) {
     public void visitBooleanConstant(EBooleanConstant userBooleanConstantNode, ScriptScope scriptScope) {
+        Class<?> valueType = scriptScope.getDecoration(userBooleanConstantNode, ValueType.class).getValueType();
+        Object constant = scriptScope.getDecoration(userBooleanConstantNode, StandardConstant.class).getStandardConstant();
+
         ConstantNode irConstantNode = new ConstantNode(userBooleanConstantNode.getLocation());
         ConstantNode irConstantNode = new ConstantNode(userBooleanConstantNode.getLocation());
-        irConstantNode.setExpressionType(scriptScope.getDecoration(userBooleanConstantNode, ValueType.class).getValueType());
-        irConstantNode.setConstant(scriptScope.getDecoration(userBooleanConstantNode, StandardConstant.class).getStandardConstant());
+        irConstantNode.attachDecoration(new IRDExpressionType(valueType));
+        irConstantNode.setConstant(constant);
 
 
         scriptScope.putDecoration(userBooleanConstantNode, new IRNodeDecoration(irConstantNode));
         scriptScope.putDecoration(userBooleanConstantNode, new IRNodeDecoration(irConstantNode));
     }
     }
 
 
     @Override
     @Override
     public void visitNumeric(ENumeric userNumericNode, ScriptScope scriptScope) {
     public void visitNumeric(ENumeric userNumericNode, ScriptScope scriptScope) {
+        Class<?> valueType = scriptScope.getDecoration(userNumericNode, ValueType.class).getValueType();
+        Object constant = scriptScope.getDecoration(userNumericNode, StandardConstant.class).getStandardConstant();
+
         ConstantNode irConstantNode = new ConstantNode(userNumericNode.getLocation());
         ConstantNode irConstantNode = new ConstantNode(userNumericNode.getLocation());
-        irConstantNode.setExpressionType(scriptScope.getDecoration(userNumericNode, ValueType.class).getValueType());
-        irConstantNode.setConstant(scriptScope.getDecoration(userNumericNode, StandardConstant.class).getStandardConstant());
+        irConstantNode.attachDecoration(new IRDExpressionType(valueType));
+        irConstantNode.setConstant(constant);
 
 
         scriptScope.putDecoration(userNumericNode, new IRNodeDecoration(irConstantNode));
         scriptScope.putDecoration(userNumericNode, new IRNodeDecoration(irConstantNode));
     }
     }
 
 
     @Override
     @Override
     public void visitDecimal(EDecimal userDecimalNode, ScriptScope scriptScope) {
     public void visitDecimal(EDecimal userDecimalNode, ScriptScope scriptScope) {
+        Class<?> valueType = scriptScope.getDecoration(userDecimalNode, ValueType.class).getValueType();
+        Object constant = scriptScope.getDecoration(userDecimalNode, StandardConstant.class).getStandardConstant();
+
         ConstantNode irConstantNode = new ConstantNode(userDecimalNode.getLocation());
         ConstantNode irConstantNode = new ConstantNode(userDecimalNode.getLocation());
-        irConstantNode.setExpressionType(scriptScope.getDecoration(userDecimalNode, ValueType.class).getValueType());
-        irConstantNode.setConstant(scriptScope.getDecoration(userDecimalNode, StandardConstant.class).getStandardConstant());
+        irConstantNode.attachDecoration(new IRDExpressionType(valueType));
+        irConstantNode.setConstant(constant);
 
 
         scriptScope.putDecoration(userDecimalNode, new IRNodeDecoration(irConstantNode));
         scriptScope.putDecoration(userDecimalNode, new IRNodeDecoration(irConstantNode));
     }
     }
 
 
     @Override
     @Override
     public void visitString(EString userStringNode, ScriptScope scriptScope) {
     public void visitString(EString userStringNode, ScriptScope scriptScope) {
+        Class<?> valueType = scriptScope.getDecoration(userStringNode, ValueType.class).getValueType();
+        Object constant = scriptScope.getDecoration(userStringNode, StandardConstant.class).getStandardConstant();
+
         ConstantNode irConstantNode = new ConstantNode(userStringNode.getLocation());
         ConstantNode irConstantNode = new ConstantNode(userStringNode.getLocation());
-        irConstantNode.setExpressionType(scriptScope.getDecoration(userStringNode, ValueType.class).getValueType());
-        irConstantNode.setConstant(scriptScope.getDecoration(userStringNode, StandardConstant.class).getStandardConstant());
+        irConstantNode.attachDecoration(new IRDExpressionType(valueType));
+        irConstantNode.setConstant(constant);
 
 
         scriptScope.putDecoration(userStringNode, new IRNodeDecoration(irConstantNode));
         scriptScope.putDecoration(userStringNode, new IRNodeDecoration(irConstantNode));
     }
     }
@@ -1195,7 +1224,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
     @Override
     @Override
     public void visitNull(ENull userNullNode, ScriptScope scriptScope) {
     public void visitNull(ENull userNullNode, ScriptScope scriptScope) {
         NullNode irNullNode = new NullNode(userNullNode.getLocation());
         NullNode irNullNode = new NullNode(userNullNode.getLocation());
-        irNullNode.setExpressionType(scriptScope.getDecoration(userNullNode, ValueType.class).getValueType());
+        irNullNode.attachDecoration(new IRDExpressionType(scriptScope.getDecoration(userNullNode, ValueType.class).getValueType()));
 
 
         scriptScope.putDecoration(userNullNode, new IRNodeDecoration(irNullNode));
         scriptScope.putDecoration(userNullNode, new IRNodeDecoration(irNullNode));
     }
     }
@@ -1218,7 +1247,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             blockNode.addStatementNode(irStatementExpressionNode);
             blockNode.addStatementNode(irStatementExpressionNode);
 
 
             StoreFieldMemberNode irStoreFieldMemberNode = new StoreFieldMemberNode(userRegexNode.getLocation());
             StoreFieldMemberNode irStoreFieldMemberNode = new StoreFieldMemberNode(userRegexNode.getLocation());
-            irStoreFieldMemberNode.setExpressionType(void.class);
+            irStoreFieldMemberNode.attachDecoration(new IRDExpressionType(void.class));
             irStoreFieldMemberNode.setStoreType(Pattern.class);
             irStoreFieldMemberNode.setStoreType(Pattern.class);
             irStoreFieldMemberNode.setName(memberFieldName);
             irStoreFieldMemberNode.setName(memberFieldName);
             irStoreFieldMemberNode.setStatic(true);
             irStoreFieldMemberNode.setStatic(true);
@@ -1226,17 +1255,17 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             irStatementExpressionNode.setExpressionNode(irStoreFieldMemberNode);
             irStatementExpressionNode.setExpressionNode(irStoreFieldMemberNode);
 
 
             BinaryImplNode irBinaryImplNode = new BinaryImplNode(userRegexNode.getLocation());
             BinaryImplNode irBinaryImplNode = new BinaryImplNode(userRegexNode.getLocation());
-            irBinaryImplNode.setExpressionType(Pattern.class);
+            irBinaryImplNode.attachDecoration(new IRDExpressionType(Pattern.class));
 
 
             irStoreFieldMemberNode.setChildNode(irBinaryImplNode);
             irStoreFieldMemberNode.setChildNode(irBinaryImplNode);
 
 
             StaticNode irStaticNode = new StaticNode(userRegexNode.getLocation());
             StaticNode irStaticNode = new StaticNode(userRegexNode.getLocation());
-            irStaticNode.setExpressionType(Pattern.class);
+            irStaticNode.attachDecoration(new IRDExpressionType(Pattern.class));
 
 
             irBinaryImplNode.setLeftNode(irStaticNode);
             irBinaryImplNode.setLeftNode(irStaticNode);
 
 
             InvokeCallNode invokeCallNode = new InvokeCallNode(userRegexNode.getLocation());
             InvokeCallNode invokeCallNode = new InvokeCallNode(userRegexNode.getLocation());
-            invokeCallNode.setExpressionType(Pattern.class);
+            invokeCallNode.attachDecoration(new IRDExpressionType(Pattern.class));
             invokeCallNode.setBox(Pattern.class);
             invokeCallNode.setBox(Pattern.class);
             invokeCallNode.setMethod(new PainlessMethod(
             invokeCallNode.setMethod(new PainlessMethod(
                             Pattern.class.getMethod("compile", String.class, int.class),
                             Pattern.class.getMethod("compile", String.class, int.class),
@@ -1252,13 +1281,13 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             irBinaryImplNode.setRightNode(invokeCallNode);
             irBinaryImplNode.setRightNode(invokeCallNode);
 
 
             ConstantNode irConstantNode = new ConstantNode(userRegexNode.getLocation());
             ConstantNode irConstantNode = new ConstantNode(userRegexNode.getLocation());
-            irConstantNode.setExpressionType(String.class);
+            irConstantNode.attachDecoration(new IRDExpressionType(String.class));
             irConstantNode.setConstant(userRegexNode.getPattern());
             irConstantNode.setConstant(userRegexNode.getPattern());
 
 
             invokeCallNode.addArgumentNode(irConstantNode);
             invokeCallNode.addArgumentNode(irConstantNode);
 
 
             irConstantNode = new ConstantNode(userRegexNode.getLocation());
             irConstantNode = new ConstantNode(userRegexNode.getLocation());
-            irConstantNode.setExpressionType(int.class);
+            irConstantNode.attachDecoration(new IRDExpressionType(int.class));
             irConstantNode.setConstant(scriptScope.getDecoration(userRegexNode, StandardConstant.class).getStandardConstant());
             irConstantNode.setConstant(scriptScope.getDecoration(userRegexNode, StandardConstant.class).getStandardConstant());
 
 
             invokeCallNode.addArgumentNode(irConstantNode);
             invokeCallNode.addArgumentNode(irConstantNode);
@@ -1267,7 +1296,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         }
         }
 
 
         LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode(userRegexNode.getLocation());
         LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode(userRegexNode.getLocation());
-        irLoadFieldMemberNode.setExpressionType(Pattern.class);
+        irLoadFieldMemberNode.attachDecoration(new IRDExpressionType(Pattern.class));
         irLoadFieldMemberNode.setName(memberFieldName);
         irLoadFieldMemberNode.setName(memberFieldName);
         irLoadFieldMemberNode.setStatic(true);
         irLoadFieldMemberNode.setStatic(true);
 
 
@@ -1301,7 +1330,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         irFunctionNode.setMaxLoopCounter(scriptScope.getCompilerSettings().getMaxLoopCounter());
         irFunctionNode.setMaxLoopCounter(scriptScope.getCompilerSettings().getMaxLoopCounter());
         irClassNode.addFunctionNode(irFunctionNode);
         irClassNode.addFunctionNode(irFunctionNode);
 
 
-        irReferenceNode.setExpressionType(scriptScope.getDecoration(userLambdaNode, ValueType.class).getValueType());
+        irReferenceNode.attachDecoration(new IRDExpressionType(scriptScope.getDecoration(userLambdaNode, ValueType.class).getValueType()));
 
 
         List<Variable> captures = scriptScope.getDecoration(userLambdaNode, CapturesDecoration.class).getCaptures();
         List<Variable> captures = scriptScope.getDecoration(userLambdaNode, CapturesDecoration.class).getCaptures();
 
 
@@ -1320,22 +1349,23 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         CapturesDecoration capturesDecoration = scriptScope.getDecoration(userFunctionRefNode, CapturesDecoration.class);
         CapturesDecoration capturesDecoration = scriptScope.getDecoration(userFunctionRefNode, CapturesDecoration.class);
 
 
         if (targetType == null) {
         if (targetType == null) {
+            String encoding = scriptScope.getDecoration(userFunctionRefNode, EncodingDecoration.class).getEncoding();
             DefInterfaceReferenceNode defInterfaceReferenceNode = new DefInterfaceReferenceNode(userFunctionRefNode.getLocation());
             DefInterfaceReferenceNode defInterfaceReferenceNode = new DefInterfaceReferenceNode(userFunctionRefNode.getLocation());
-            defInterfaceReferenceNode.setDefReferenceEncoding(
-                    scriptScope.getDecoration(userFunctionRefNode, EncodingDecoration.class).getEncoding());
+            defInterfaceReferenceNode.setDefReferenceEncoding(encoding);
             irReferenceNode = defInterfaceReferenceNode;
             irReferenceNode = defInterfaceReferenceNode;
         } else if (capturesDecoration != null && capturesDecoration.getCaptures().get(0).getType() == def.class) {
         } else if (capturesDecoration != null && capturesDecoration.getCaptures().get(0).getType() == def.class) {
             TypedCaptureReferenceNode typedCaptureReferenceNode = new TypedCaptureReferenceNode(userFunctionRefNode.getLocation());
             TypedCaptureReferenceNode typedCaptureReferenceNode = new TypedCaptureReferenceNode(userFunctionRefNode.getLocation());
             typedCaptureReferenceNode.setMethodName(userFunctionRefNode.getMethodName());
             typedCaptureReferenceNode.setMethodName(userFunctionRefNode.getMethodName());
             irReferenceNode = typedCaptureReferenceNode;
             irReferenceNode = typedCaptureReferenceNode;
         } else {
         } else {
+            FunctionRef reference = scriptScope.getDecoration(userFunctionRefNode, ReferenceDecoration.class).getReference();
             TypedInterfaceReferenceNode typedInterfaceReferenceNode = new TypedInterfaceReferenceNode(userFunctionRefNode.getLocation());
             TypedInterfaceReferenceNode typedInterfaceReferenceNode = new TypedInterfaceReferenceNode(userFunctionRefNode.getLocation());
-            typedInterfaceReferenceNode.setReference(
-                    scriptScope.getDecoration(userFunctionRefNode, ReferenceDecoration.class).getReference());
+            typedInterfaceReferenceNode.setReference(reference);
             irReferenceNode = typedInterfaceReferenceNode;
             irReferenceNode = typedInterfaceReferenceNode;
         }
         }
 
 
-        irReferenceNode.setExpressionType(scriptScope.getDecoration(userFunctionRefNode, ValueType.class).getValueType());
+        irReferenceNode.attachDecoration(
+                new IRDExpressionType(scriptScope.getDecoration(userFunctionRefNode, ValueType.class).getValueType()));
 
 
         if (capturesDecoration != null) {
         if (capturesDecoration != null) {
             irReferenceNode.addCapture(capturesDecoration.getCaptures().get(0).getName());
             irReferenceNode.addCapture(capturesDecoration.getCaptures().get(0).getName());
@@ -1351,24 +1381,24 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         if (scriptScope.hasDecoration(userNewArrayFunctionRefNode, TargetType.class)) {
         if (scriptScope.hasDecoration(userNewArrayFunctionRefNode, TargetType.class)) {
             TypedInterfaceReferenceNode typedInterfaceReferenceNode =
             TypedInterfaceReferenceNode typedInterfaceReferenceNode =
                     new TypedInterfaceReferenceNode(userNewArrayFunctionRefNode.getLocation());
                     new TypedInterfaceReferenceNode(userNewArrayFunctionRefNode.getLocation());
-            typedInterfaceReferenceNode.setReference(
-                    scriptScope.getDecoration(userNewArrayFunctionRefNode, ReferenceDecoration.class).getReference());
+            FunctionRef reference = scriptScope.getDecoration(userNewArrayFunctionRefNode, ReferenceDecoration.class).getReference();
+            typedInterfaceReferenceNode.setReference(reference);
             irReferenceNode = typedInterfaceReferenceNode;
             irReferenceNode = typedInterfaceReferenceNode;
         } else {
         } else {
+            String encoding = scriptScope.getDecoration(userNewArrayFunctionRefNode, EncodingDecoration.class).getEncoding();
             DefInterfaceReferenceNode defInterfaceReferenceNode = new DefInterfaceReferenceNode(userNewArrayFunctionRefNode.getLocation());
             DefInterfaceReferenceNode defInterfaceReferenceNode = new DefInterfaceReferenceNode(userNewArrayFunctionRefNode.getLocation());
-            defInterfaceReferenceNode.setDefReferenceEncoding(
-                    scriptScope.getDecoration(userNewArrayFunctionRefNode, EncodingDecoration.class).getEncoding());
+            defInterfaceReferenceNode.setDefReferenceEncoding(encoding);
             irReferenceNode = defInterfaceReferenceNode;
             irReferenceNode = defInterfaceReferenceNode;
         }
         }
 
 
         Class<?> returnType = scriptScope.getDecoration(userNewArrayFunctionRefNode, ReturnType.class).getReturnType();
         Class<?> returnType = scriptScope.getDecoration(userNewArrayFunctionRefNode, ReturnType.class).getReturnType();
 
 
         LoadVariableNode irLoadVariableNode = new LoadVariableNode(userNewArrayFunctionRefNode.getLocation());
         LoadVariableNode irLoadVariableNode = new LoadVariableNode(userNewArrayFunctionRefNode.getLocation());
-        irLoadVariableNode.setExpressionType(int.class);
+        irLoadVariableNode.attachDecoration(new IRDExpressionType(int.class));
         irLoadVariableNode.setName("size");
         irLoadVariableNode.setName("size");
 
 
         NewArrayNode irNewArrayNode = new NewArrayNode(userNewArrayFunctionRefNode.getLocation());
         NewArrayNode irNewArrayNode = new NewArrayNode(userNewArrayFunctionRefNode.getLocation());
-        irNewArrayNode.setExpressionType(returnType);
+        irNewArrayNode.attachDecoration(new IRDExpressionType(returnType));
         irNewArrayNode.setInitialize(false);
         irNewArrayNode.setInitialize(false);
 
 
         irNewArrayNode.addArgumentNode(irLoadVariableNode);
         irNewArrayNode.addArgumentNode(irLoadVariableNode);
@@ -1393,7 +1423,8 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
         irClassNode.addFunctionNode(irFunctionNode);
         irClassNode.addFunctionNode(irFunctionNode);
 
 
-        irReferenceNode.setExpressionType(scriptScope.getDecoration(userNewArrayFunctionRefNode, ValueType.class).getValueType());
+        irReferenceNode.attachDecoration(
+                new IRDExpressionType(scriptScope.getDecoration(userNewArrayFunctionRefNode, ValueType.class).getValueType()));
 
 
         scriptScope.putDecoration(userNewArrayFunctionRefNode, new IRNodeDecoration(irReferenceNode));
         scriptScope.putDecoration(userNewArrayFunctionRefNode, new IRNodeDecoration(irReferenceNode));
     }
     }
@@ -1409,7 +1440,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         if (scriptScope.hasDecoration(userSymbolNode, StaticType.class)) {
         if (scriptScope.hasDecoration(userSymbolNode, StaticType.class)) {
             Class<?> staticType = scriptScope.getDecoration(userSymbolNode, StaticType.class).getStaticType();
             Class<?> staticType = scriptScope.getDecoration(userSymbolNode, StaticType.class).getStaticType();
             StaticNode staticNode = new StaticNode(userSymbolNode.getLocation());
             StaticNode staticNode = new StaticNode(userSymbolNode.getLocation());
-            staticNode.setExpressionType(staticType);
+            staticNode.attachDecoration(new IRDExpressionType(staticType));
             irExpressionNode = staticNode;
             irExpressionNode = staticNode;
         } else if (scriptScope.hasDecoration(userSymbolNode, ValueType.class)) {
         } else if (scriptScope.hasDecoration(userSymbolNode, ValueType.class)) {
             boolean read = scriptScope.getCondition(userSymbolNode, Read.class);
             boolean read = scriptScope.getCondition(userSymbolNode, Read.class);
@@ -1424,7 +1455,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
             if (write || compound) {
             if (write || compound) {
                 StoreVariableNode irStoreVariableNode = new StoreVariableNode(location);
                 StoreVariableNode irStoreVariableNode = new StoreVariableNode(location);
-                irStoreVariableNode.setExpressionType(read ? valueType : void.class);
+                irStoreVariableNode.attachDecoration(new IRDExpressionType(read ? valueType : void.class));
                 irStoreVariableNode.setStoreType(valueType);
                 irStoreVariableNode.setStoreType(valueType);
                 irStoreVariableNode.setName(symbol);
                 irStoreVariableNode.setName(symbol);
                 irStoreNode = irStoreVariableNode;
                 irStoreNode = irStoreVariableNode;
@@ -1432,7 +1463,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
             if (write == false || compound) {
             if (write == false || compound) {
                 LoadVariableNode irLoadVariableNode = new LoadVariableNode(location);
                 LoadVariableNode irLoadVariableNode = new LoadVariableNode(location);
-                irLoadVariableNode.setExpressionType(valueType);
+                irLoadVariableNode.attachDecoration(new IRDExpressionType(valueType));
                 irLoadVariableNode.setName(symbol);
                 irLoadVariableNode.setName(symbol);
                 irLoadNode = irLoadVariableNode;
                 irLoadNode = irLoadVariableNode;
             }
             }
@@ -1457,7 +1488,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         if (scriptScope.hasDecoration(userDotNode, StaticType.class)) {
         if (scriptScope.hasDecoration(userDotNode, StaticType.class)) {
             Class<?> staticType = scriptScope.getDecoration(userDotNode, StaticType.class).getStaticType();
             Class<?> staticType = scriptScope.getDecoration(userDotNode, StaticType.class).getStaticType();
             StaticNode staticNode = new StaticNode(userDotNode.getLocation());
             StaticNode staticNode = new StaticNode(userDotNode.getLocation());
-            staticNode.setExpressionType(staticType);
+            staticNode.attachDecoration(new IRDExpressionType(staticType));
             irExpressionNode = staticNode;
             irExpressionNode = staticNode;
         } else {
         } else {
             boolean read = scriptScope.getCondition(userDotNode, Read.class);
             boolean read = scriptScope.getCondition(userDotNode, Read.class);
@@ -1476,14 +1507,14 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
             if (prefixValueType != null && prefixValueType.getValueType().isArray()) {
             if (prefixValueType != null && prefixValueType.getValueType().isArray()) {
                 LoadDotArrayLengthNode irLoadDotArrayLengthNode = new LoadDotArrayLengthNode(location);
                 LoadDotArrayLengthNode irLoadDotArrayLengthNode = new LoadDotArrayLengthNode(location);
-                irLoadDotArrayLengthNode.setExpressionType(int.class);
+                irLoadDotArrayLengthNode.attachDecoration(new IRDExpressionType(int.class));
                 irLoadNode = irLoadDotArrayLengthNode;
                 irLoadNode = irLoadDotArrayLengthNode;
 
 
                 accessDepth = 1;
                 accessDepth = 1;
             } else if (prefixValueType != null && prefixValueType.getValueType() == def.class) {
             } else if (prefixValueType != null && prefixValueType.getValueType() == def.class) {
                 if (write || compound) {
                 if (write || compound) {
                     StoreDotDefNode irStoreDotDefNode = new StoreDotDefNode(location);
                     StoreDotDefNode irStoreDotDefNode = new StoreDotDefNode(location);
-                    irStoreDotDefNode.setExpressionType(read ? valueType : void.class);
+                    irStoreDotDefNode.attachDecoration(new IRDExpressionType(read ? valueType : void.class));
                     irStoreDotDefNode.setStoreType(valueType);
                     irStoreDotDefNode.setStoreType(valueType);
                     irStoreDotDefNode.setValue(index);
                     irStoreDotDefNode.setValue(index);
                     irStoreNode = irStoreDotDefNode;
                     irStoreNode = irStoreDotDefNode;
@@ -1491,7 +1522,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
                 if (write == false || compound) {
                 if (write == false || compound) {
                     LoadDotDefNode irLoadDotDefNode = new LoadDotDefNode(location);
                     LoadDotDefNode irLoadDotDefNode = new LoadDotDefNode(location);
-                    irLoadDotDefNode.setExpressionType(valueType);
+                    irLoadDotDefNode.attachDecoration(new IRDExpressionType(valueType));
                     irLoadDotDefNode.setValue(index);
                     irLoadDotDefNode.setValue(index);
                     irLoadNode = irLoadDotDefNode;
                     irLoadNode = irLoadDotDefNode;
                 }
                 }
@@ -1503,7 +1534,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
                 if (write || compound) {
                 if (write || compound) {
                     StoreDotNode irStoreDotNode = new StoreDotNode(location);
                     StoreDotNode irStoreDotNode = new StoreDotNode(location);
-                    irStoreDotNode.setExpressionType(read ? valueType : void.class);
+                    irStoreDotNode.attachDecoration(new IRDExpressionType(read ? valueType : void.class));
                     irStoreDotNode.setStoreType(valueType);
                     irStoreDotNode.setStoreType(valueType);
                     irStoreDotNode.setField(painlessField);
                     irStoreDotNode.setField(painlessField);
                     irStoreNode = irStoreDotNode;
                     irStoreNode = irStoreDotNode;
@@ -1511,7 +1542,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
                 if (write == false || compound) {
                 if (write == false || compound) {
                     LoadDotNode irLoadDotNode = new LoadDotNode(location);
                     LoadDotNode irLoadDotNode = new LoadDotNode(location);
-                    irLoadDotNode.setExpressionType(valueType);
+                    irLoadDotNode.attachDecoration(new IRDExpressionType(valueType));
                     irLoadDotNode.setField(painlessField);
                     irLoadDotNode.setField(painlessField);
                     irLoadNode = irLoadDotNode;
                     irLoadNode = irLoadDotNode;
                 }
                 }
@@ -1520,7 +1551,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
             } else if (scriptScope.getCondition(userDotNode, Shortcut.class)) {
             } else if (scriptScope.getCondition(userDotNode, Shortcut.class)) {
                 if (write || compound) {
                 if (write || compound) {
                     StoreDotShortcutNode irStoreDotShortcutNode = new StoreDotShortcutNode(location);
                     StoreDotShortcutNode irStoreDotShortcutNode = new StoreDotShortcutNode(location);
-                    irStoreDotShortcutNode.setExpressionType(read ? valueType : void.class);
+                    irStoreDotShortcutNode.attachDecoration(new IRDExpressionType(read ? valueType : void.class));
                     irStoreDotShortcutNode.setStoreType(valueType);
                     irStoreDotShortcutNode.setStoreType(valueType);
                     irStoreDotShortcutNode.setSetter(
                     irStoreDotShortcutNode.setSetter(
                             scriptScope.getDecoration(userDotNode, SetterPainlessMethod.class).getSetterPainlessMethod());
                             scriptScope.getDecoration(userDotNode, SetterPainlessMethod.class).getSetterPainlessMethod());
@@ -1529,7 +1560,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
                 if (write == false || compound) {
                 if (write == false || compound) {
                     LoadDotShortcutNode irLoadDotShortcutNode = new LoadDotShortcutNode(location);
                     LoadDotShortcutNode irLoadDotShortcutNode = new LoadDotShortcutNode(location);
-                    irLoadDotShortcutNode.setExpressionType(valueType);
+                    irLoadDotShortcutNode.attachDecoration(new IRDExpressionType(valueType));
                     irLoadDotShortcutNode.setGetter(
                     irLoadDotShortcutNode.setGetter(
                             scriptScope.getDecoration(userDotNode, GetterPainlessMethod.class).getGetterPainlessMethod());
                             scriptScope.getDecoration(userDotNode, GetterPainlessMethod.class).getGetterPainlessMethod());
                     irLoadNode = irLoadDotShortcutNode;
                     irLoadNode = irLoadDotShortcutNode;
@@ -1538,13 +1569,13 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 accessDepth = 1;
                 accessDepth = 1;
             } else if (scriptScope.getCondition(userDotNode, MapShortcut.class)) {
             } else if (scriptScope.getCondition(userDotNode, MapShortcut.class)) {
                 ConstantNode irConstantNode = new ConstantNode(location);
                 ConstantNode irConstantNode = new ConstantNode(location);
-                irConstantNode.setExpressionType(String.class);
+                irConstantNode.attachDecoration(new IRDExpressionType(String.class));
                 irConstantNode.setConstant(index);
                 irConstantNode.setConstant(index);
                 irIndexNode = irConstantNode;
                 irIndexNode = irConstantNode;
 
 
                 if (write || compound) {
                 if (write || compound) {
                     StoreMapShortcutNode irStoreMapShortcutNode = new StoreMapShortcutNode(location);
                     StoreMapShortcutNode irStoreMapShortcutNode = new StoreMapShortcutNode(location);
-                    irStoreMapShortcutNode.setExpressionType(read ? valueType : void.class);
+                    irStoreMapShortcutNode.attachDecoration(new IRDExpressionType(read ? valueType : void.class));
                     irStoreMapShortcutNode.setStoreType(valueType);
                     irStoreMapShortcutNode.setStoreType(valueType);
                     irStoreMapShortcutNode.setSetter(
                     irStoreMapShortcutNode.setSetter(
                             scriptScope.getDecoration(userDotNode, SetterPainlessMethod.class).getSetterPainlessMethod());
                             scriptScope.getDecoration(userDotNode, SetterPainlessMethod.class).getSetterPainlessMethod());
@@ -1553,7 +1584,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
                 if (write == false || compound) {
                 if (write == false || compound) {
                     LoadMapShortcutNode irLoadMapShortcutNode = new LoadMapShortcutNode(location);
                     LoadMapShortcutNode irLoadMapShortcutNode = new LoadMapShortcutNode(location);
-                    irLoadMapShortcutNode.setExpressionType(valueType);
+                    irLoadMapShortcutNode.attachDecoration(new IRDExpressionType(valueType));
                     irLoadMapShortcutNode.setGetter(
                     irLoadMapShortcutNode.setGetter(
                             scriptScope.getDecoration(userDotNode, GetterPainlessMethod.class).getGetterPainlessMethod());
                             scriptScope.getDecoration(userDotNode, GetterPainlessMethod.class).getGetterPainlessMethod());
                     irLoadNode = irLoadMapShortcutNode;
                     irLoadNode = irLoadMapShortcutNode;
@@ -1562,13 +1593,13 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 accessDepth = 2;
                 accessDepth = 2;
             } else if (scriptScope.getCondition(userDotNode, ListShortcut.class)) {
             } else if (scriptScope.getCondition(userDotNode, ListShortcut.class)) {
                 ConstantNode irConstantNode = new ConstantNode(location);
                 ConstantNode irConstantNode = new ConstantNode(location);
-                irConstantNode.setExpressionType(int.class);
+                irConstantNode.attachDecoration(new IRDExpressionType(int.class));
                 irConstantNode.setConstant(scriptScope.getDecoration(userDotNode, StandardConstant.class).getStandardConstant());
                 irConstantNode.setConstant(scriptScope.getDecoration(userDotNode, StandardConstant.class).getStandardConstant());
                 irIndexNode = irConstantNode;
                 irIndexNode = irConstantNode;
 
 
                 if (write || compound) {
                 if (write || compound) {
                     StoreListShortcutNode irStoreListShortcutNode = new StoreListShortcutNode(location);
                     StoreListShortcutNode irStoreListShortcutNode = new StoreListShortcutNode(location);
-                    irStoreListShortcutNode.setExpressionType(read ? valueType : void.class);
+                    irStoreListShortcutNode.attachDecoration(new IRDExpressionType(read ? valueType : void.class));
                     irStoreListShortcutNode.setStoreType(valueType);
                     irStoreListShortcutNode.setStoreType(valueType);
                     irStoreListShortcutNode.setSetter(
                     irStoreListShortcutNode.setSetter(
                             scriptScope.getDecoration(userDotNode, SetterPainlessMethod.class).getSetterPainlessMethod());
                             scriptScope.getDecoration(userDotNode, SetterPainlessMethod.class).getSetterPainlessMethod());
@@ -1577,7 +1608,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
                 if (write == false || compound) {
                 if (write == false || compound) {
                     LoadListShortcutNode irLoadListShortcutNode = new LoadListShortcutNode(location);
                     LoadListShortcutNode irLoadListShortcutNode = new LoadListShortcutNode(location);
-                    irLoadListShortcutNode.setExpressionType(valueType);
+                    irLoadListShortcutNode.attachDecoration(new IRDExpressionType(valueType));
                     irLoadListShortcutNode.setGetter(
                     irLoadListShortcutNode.setGetter(
                             scriptScope.getDecoration(userDotNode, GetterPainlessMethod.class).getGetterPainlessMethod());
                             scriptScope.getDecoration(userDotNode, GetterPainlessMethod.class).getGetterPainlessMethod());
                     irLoadNode = irLoadListShortcutNode;
                     irLoadNode = irLoadListShortcutNode;
@@ -1616,32 +1647,32 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
         if (prefixValueType.isArray()) {
         if (prefixValueType.isArray()) {
             FlipArrayIndexNode irFlipArrayIndexNode = new FlipArrayIndexNode(userBraceNode.getIndexNode().getLocation());
             FlipArrayIndexNode irFlipArrayIndexNode = new FlipArrayIndexNode(userBraceNode.getIndexNode().getLocation());
-            irFlipArrayIndexNode.setExpressionType(int.class);
+            irFlipArrayIndexNode.attachDecoration(new IRDExpressionType(int.class));
             irFlipArrayIndexNode.setChildNode(irIndexNode);
             irFlipArrayIndexNode.setChildNode(irIndexNode);
             irIndexNode = irFlipArrayIndexNode;
             irIndexNode = irFlipArrayIndexNode;
 
 
             if (write || compound) {
             if (write || compound) {
                 StoreBraceNode irStoreBraceNode = new StoreBraceNode(location);
                 StoreBraceNode irStoreBraceNode = new StoreBraceNode(location);
-                irStoreBraceNode.setExpressionType(read ? valueType : void.class);
+                irStoreBraceNode.attachDecoration(new IRDExpressionType(read ? valueType : void.class));
                 irStoreBraceNode.setStoreType(valueType);
                 irStoreBraceNode.setStoreType(valueType);
                 irStoreNode = irStoreBraceNode;
                 irStoreNode = irStoreBraceNode;
             }
             }
 
 
             if (write == false || compound) {
             if (write == false || compound) {
                 LoadBraceNode irLoadBraceNode = new LoadBraceNode(location);
                 LoadBraceNode irLoadBraceNode = new LoadBraceNode(location);
-                irLoadBraceNode.setExpressionType(valueType);
+                irLoadBraceNode.attachDecoration(new IRDExpressionType(valueType));
                 irLoadNode = irLoadBraceNode;
                 irLoadNode = irLoadBraceNode;
             }
             }
         } else if (prefixValueType == def.class) {
         } else if (prefixValueType == def.class) {
             Class<?> indexType = scriptScope.getDecoration(userBraceNode.getIndexNode(), ValueType.class).getValueType();
             Class<?> indexType = scriptScope.getDecoration(userBraceNode.getIndexNode(), ValueType.class).getValueType();
             FlipDefIndexNode irFlipDefIndexNode = new FlipDefIndexNode(userBraceNode.getIndexNode().getLocation());
             FlipDefIndexNode irFlipDefIndexNode = new FlipDefIndexNode(userBraceNode.getIndexNode().getLocation());
-            irFlipDefIndexNode.setExpressionType(indexType);
+            irFlipDefIndexNode.attachDecoration(new IRDExpressionType(indexType));
             irFlipDefIndexNode.setChildNode(irIndexNode);
             irFlipDefIndexNode.setChildNode(irIndexNode);
             irIndexNode = irFlipDefIndexNode;
             irIndexNode = irFlipDefIndexNode;
 
 
             if (write || compound) {
             if (write || compound) {
                 StoreBraceDefNode irStoreBraceNode = new StoreBraceDefNode(location);
                 StoreBraceDefNode irStoreBraceNode = new StoreBraceDefNode(location);
-                irStoreBraceNode.setExpressionType(read ? valueType : void.class);
+                irStoreBraceNode.attachDecoration(new IRDExpressionType(read ? valueType : void.class));
                 irStoreBraceNode.setStoreType(valueType);
                 irStoreBraceNode.setStoreType(valueType);
                 irStoreBraceNode.setIndexType(indexType);
                 irStoreBraceNode.setIndexType(indexType);
                 irStoreNode = irStoreBraceNode;
                 irStoreNode = irStoreBraceNode;
@@ -1649,47 +1680,47 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
 
 
             if (write == false || compound) {
             if (write == false || compound) {
                 LoadBraceDefNode irLoadBraceDefNode = new LoadBraceDefNode(location);
                 LoadBraceDefNode irLoadBraceDefNode = new LoadBraceDefNode(location);
-                irLoadBraceDefNode.setExpressionType(valueType);
+                irLoadBraceDefNode.attachDecoration(new IRDExpressionType(valueType));
                 irLoadBraceDefNode.setIndexType(indexType);
                 irLoadBraceDefNode.setIndexType(indexType);
                 irLoadNode = irLoadBraceDefNode;
                 irLoadNode = irLoadBraceDefNode;
             }
             }
         } else if (scriptScope.getCondition(userBraceNode, MapShortcut.class)) {
         } else if (scriptScope.getCondition(userBraceNode, MapShortcut.class)) {
             if (write || compound) {
             if (write || compound) {
+                PainlessMethod setter = scriptScope.getDecoration(userBraceNode, SetterPainlessMethod.class).getSetterPainlessMethod();
                 StoreMapShortcutNode irStoreMapShortcutNode = new StoreMapShortcutNode(location);
                 StoreMapShortcutNode irStoreMapShortcutNode = new StoreMapShortcutNode(location);
-                irStoreMapShortcutNode.setExpressionType(read ? valueType : void.class);
+                irStoreMapShortcutNode.attachDecoration(new IRDExpressionType(read ? valueType : void.class));
                 irStoreMapShortcutNode.setStoreType(valueType);
                 irStoreMapShortcutNode.setStoreType(valueType);
-                irStoreMapShortcutNode.setSetter(
-                        scriptScope.getDecoration(userBraceNode, SetterPainlessMethod.class).getSetterPainlessMethod());
+                irStoreMapShortcutNode.setSetter(setter);
                 irStoreNode = irStoreMapShortcutNode;
                 irStoreNode = irStoreMapShortcutNode;
             }
             }
 
 
             if (write == false || compound) {
             if (write == false || compound) {
+                PainlessMethod getter = scriptScope.getDecoration(userBraceNode, GetterPainlessMethod.class).getGetterPainlessMethod();
                 LoadMapShortcutNode irLoadMapShortcutNode = new LoadMapShortcutNode(location);
                 LoadMapShortcutNode irLoadMapShortcutNode = new LoadMapShortcutNode(location);
-                irLoadMapShortcutNode.setExpressionType(scriptScope.getDecoration(userBraceNode, ValueType.class).getValueType());
-                irLoadMapShortcutNode.setGetter(
-                        scriptScope.getDecoration(userBraceNode, GetterPainlessMethod.class).getGetterPainlessMethod());
+                irLoadMapShortcutNode.attachDecoration(new IRDExpressionType(valueType));
+                irLoadMapShortcutNode.setGetter(getter);
                 irLoadNode = irLoadMapShortcutNode;
                 irLoadNode = irLoadMapShortcutNode;
             }
             }
         } else if (scriptScope.getCondition(userBraceNode, ListShortcut.class)) {
         } else if (scriptScope.getCondition(userBraceNode, ListShortcut.class)) {
             FlipCollectionIndexNode irFlipCollectionIndexNode = new FlipCollectionIndexNode(userBraceNode.getIndexNode().getLocation());
             FlipCollectionIndexNode irFlipCollectionIndexNode = new FlipCollectionIndexNode(userBraceNode.getIndexNode().getLocation());
-            irFlipCollectionIndexNode.setExpressionType(int.class);
+            irFlipCollectionIndexNode.attachDecoration(new IRDExpressionType(int.class));
             irFlipCollectionIndexNode.setChildNode(irIndexNode);
             irFlipCollectionIndexNode.setChildNode(irIndexNode);
             irIndexNode = irFlipCollectionIndexNode;
             irIndexNode = irFlipCollectionIndexNode;
 
 
             if (write || compound) {
             if (write || compound) {
+                PainlessMethod setter = scriptScope.getDecoration(userBraceNode, SetterPainlessMethod.class).getSetterPainlessMethod();
                 StoreListShortcutNode irStoreListShortcutNode = new StoreListShortcutNode(location);
                 StoreListShortcutNode irStoreListShortcutNode = new StoreListShortcutNode(location);
-                irStoreListShortcutNode.setExpressionType(read ? valueType : void.class);
+                irStoreListShortcutNode.attachDecoration(new IRDExpressionType(read ? valueType : void.class));
                 irStoreListShortcutNode.setStoreType(valueType);
                 irStoreListShortcutNode.setStoreType(valueType);
-                irStoreListShortcutNode.setSetter(
-                        scriptScope.getDecoration(userBraceNode, SetterPainlessMethod.class).getSetterPainlessMethod());
+                irStoreListShortcutNode.setSetter(setter);
                 irStoreNode = irStoreListShortcutNode;
                 irStoreNode = irStoreListShortcutNode;
             }
             }
 
 
             if (write == false || compound) {
             if (write == false || compound) {
+                PainlessMethod getter = scriptScope.getDecoration(userBraceNode, GetterPainlessMethod.class).getGetterPainlessMethod();
                 LoadListShortcutNode irLoadListShortcutNode = new LoadListShortcutNode(location);
                 LoadListShortcutNode irLoadListShortcutNode = new LoadListShortcutNode(location);
-                irLoadListShortcutNode.setExpressionType(scriptScope.getDecoration(userBraceNode, ValueType.class).getValueType());
-                irLoadListShortcutNode.setGetter(
-                        scriptScope.getDecoration(userBraceNode, GetterPainlessMethod.class).getGetterPainlessMethod());
+                irLoadListShortcutNode.attachDecoration(new IRDExpressionType(valueType));
+                irLoadListShortcutNode.setGetter(getter);
                 irLoadNode = irLoadListShortcutNode;
                 irLoadNode = irLoadListShortcutNode;
             }
             }
         } else {
         } else {
@@ -1707,6 +1738,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         ExpressionNode irExpressionNode;
         ExpressionNode irExpressionNode;
 
 
         ValueType prefixValueType = scriptScope.getDecoration(userCallNode.getPrefixNode(), ValueType.class);
         ValueType prefixValueType = scriptScope.getDecoration(userCallNode.getPrefixNode(), ValueType.class);
+        Class<?> valueType = scriptScope.getDecoration(userCallNode, ValueType.class).getValueType();
 
 
         if (prefixValueType != null && prefixValueType.getValueType() == def.class) {
         if (prefixValueType != null && prefixValueType.getValueType() == def.class) {
             InvokeCallDefNode irCallSubDefNode = new InvokeCallDefNode(userCallNode.getLocation());
             InvokeCallDefNode irCallSubDefNode = new InvokeCallDefNode(userCallNode.getLocation());
@@ -1715,7 +1747,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 irCallSubDefNode.addArgumentNode((ExpressionNode)visit(userArgumentNode, scriptScope));
                 irCallSubDefNode.addArgumentNode((ExpressionNode)visit(userArgumentNode, scriptScope));
             }
             }
 
 
-            irCallSubDefNode.setExpressionType(scriptScope.getDecoration(userCallNode, ValueType.class).getValueType());
+            irCallSubDefNode.attachDecoration(new IRDExpressionType(valueType));
             irCallSubDefNode.setName(userCallNode.getMethodName());
             irCallSubDefNode.setName(userCallNode.getMethodName());
             irExpressionNode = irCallSubDefNode;
             irExpressionNode = irCallSubDefNode;
         } else {
         } else {
@@ -1742,7 +1774,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 }
                 }
 
 
                 ConstantNode constantNode = new ConstantNode(userCallNode.getLocation());
                 ConstantNode constantNode = new ConstantNode(userCallNode.getLocation());
-                constantNode.setExpressionType(parameterType);
+                constantNode.attachDecoration(new IRDExpressionType(parameterType));
                 constantNode.setConstant(injection);
                 constantNode.setConstant(injection);
                 irInvokeCallNode.addArgumentNode(constantNode);
                 irInvokeCallNode.addArgumentNode(constantNode);
             }
             }
@@ -1751,7 +1783,7 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
                 irInvokeCallNode.addArgumentNode(injectCast(userCallArgumentNode, scriptScope));
                 irInvokeCallNode.addArgumentNode(injectCast(userCallArgumentNode, scriptScope));
             }
             }
 
 
-            irInvokeCallNode.setExpressionType(scriptScope.getDecoration(userCallNode, ValueType.class).getValueType());;
+            irInvokeCallNode.attachDecoration(new IRDExpressionType(valueType));
             irInvokeCallNode.setMethod(scriptScope.getDecoration(userCallNode, StandardPainlessMethod.class).getStandardPainlessMethod());
             irInvokeCallNode.setMethod(scriptScope.getDecoration(userCallNode, StandardPainlessMethod.class).getStandardPainlessMethod());
             irInvokeCallNode.setBox(boxType);
             irInvokeCallNode.setBox(boxType);
             irExpressionNode = irInvokeCallNode;
             irExpressionNode = irInvokeCallNode;
@@ -1760,14 +1792,14 @@ public class DefaultUserTreeToIRTreePhase implements UserTreeVisitor<ScriptScope
         if (userCallNode.isNullSafe()) {
         if (userCallNode.isNullSafe()) {
             NullSafeSubNode irNullSafeSubNode = new NullSafeSubNode(irExpressionNode.getLocation());
             NullSafeSubNode irNullSafeSubNode = new NullSafeSubNode(irExpressionNode.getLocation());
             irNullSafeSubNode.setChildNode(irExpressionNode);
             irNullSafeSubNode.setChildNode(irExpressionNode);
-            irNullSafeSubNode.setExpressionType(irExpressionNode.getExpressionType());
+            irNullSafeSubNode.copyDecorationFrom(irExpressionNode, IRDExpressionType.class);
             irExpressionNode = irNullSafeSubNode;
             irExpressionNode = irNullSafeSubNode;
         }
         }
 
 
         BinaryImplNode irBinaryImplNode = new BinaryImplNode(irExpressionNode.getLocation());
         BinaryImplNode irBinaryImplNode = new BinaryImplNode(irExpressionNode.getLocation());
         irBinaryImplNode.setLeftNode((ExpressionNode)visit(userCallNode.getPrefixNode(), scriptScope));
         irBinaryImplNode.setLeftNode((ExpressionNode)visit(userCallNode.getPrefixNode(), scriptScope));
         irBinaryImplNode.setRightNode(irExpressionNode);
         irBinaryImplNode.setRightNode(irExpressionNode);
-        irBinaryImplNode.setExpressionType(irExpressionNode.getExpressionType());
+        irBinaryImplNode.copyDecorationFrom(irExpressionNode, IRDExpressionType.class);
 
 
         scriptScope.putDecoration(userCallNode, new IRNodeDecoration(irBinaryImplNode));
         scriptScope.putDecoration(userCallNode, new IRNodeDecoration(irBinaryImplNode));
     }
     }

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

@@ -52,6 +52,7 @@ import org.elasticsearch.painless.symbol.Decorations.Converter;
 import org.elasticsearch.painless.symbol.Decorations.IRNodeDecoration;
 import org.elasticsearch.painless.symbol.Decorations.IRNodeDecoration;
 import org.elasticsearch.painless.symbol.Decorations.MethodEscape;
 import org.elasticsearch.painless.symbol.Decorations.MethodEscape;
 import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction;
 import org.elasticsearch.painless.symbol.FunctionTable.LocalFunction;
+import org.elasticsearch.painless.symbol.IRDecorations.IRDExpressionType;
 import org.elasticsearch.painless.symbol.ScriptScope;
 import org.elasticsearch.painless.symbol.ScriptScope;
 import org.elasticsearch.script.ScriptException;
 import org.elasticsearch.script.ScriptException;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Opcodes;
@@ -92,7 +93,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
                 } else {
                 } else {
                     if (returnType.isPrimitive()) {
                     if (returnType.isPrimitive()) {
                         ConstantNode irConstantNode = new ConstantNode(userFunctionNode.getLocation());
                         ConstantNode irConstantNode = new ConstantNode(userFunctionNode.getLocation());
-                        irConstantNode.setExpressionType(returnType);
+                        irConstantNode.attachDecoration(new IRDExpressionType(returnType));
 
 
                         if (returnType == boolean.class) {
                         if (returnType == boolean.class) {
                             irConstantNode.setConstant(false);
                             irConstantNode.setConstant(false);
@@ -114,7 +115,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
                         irExpressionNode = irConstantNode;
                         irExpressionNode = irConstantNode;
                     } else {
                     } else {
                         irExpressionNode = new NullNode(userFunctionNode.getLocation());
                         irExpressionNode = new NullNode(userFunctionNode.getLocation());
-                        irExpressionNode.setExpressionType(returnType);
+                        irExpressionNode.attachDecoration(new IRDExpressionType(returnType));
                     }
                     }
                 }
                 }
 
 
@@ -198,7 +199,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
         irBlockNode.addStatementNode(irReturnNode);
         irBlockNode.addStatementNode(irReturnNode);
 
 
         LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
         LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
-        irLoadFieldMemberNode.setExpressionType(String.class);
+        irLoadFieldMemberNode.attachDecoration(new IRDExpressionType(String.class));
         irLoadFieldMemberNode.setName("$NAME");
         irLoadFieldMemberNode.setName("$NAME");
         irLoadFieldMemberNode.setStatic(true);
         irLoadFieldMemberNode.setStatic(true);
 
 
@@ -224,7 +225,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
         irBlockNode.addStatementNode(irReturnNode);
         irBlockNode.addStatementNode(irReturnNode);
 
 
         irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
         irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
-        irLoadFieldMemberNode.setExpressionType(String.class);
+        irLoadFieldMemberNode.attachDecoration(new IRDExpressionType(String.class));
         irLoadFieldMemberNode.setName("$SOURCE");
         irLoadFieldMemberNode.setName("$SOURCE");
         irLoadFieldMemberNode.setStatic(true);
         irLoadFieldMemberNode.setStatic(true);
 
 
@@ -250,7 +251,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
         irBlockNode.addStatementNode(irReturnNode);
         irBlockNode.addStatementNode(irReturnNode);
 
 
         irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
         irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
-        irLoadFieldMemberNode.setExpressionType(BitSet.class);
+        irLoadFieldMemberNode.attachDecoration(new IRDExpressionType(BitSet.class));
         irLoadFieldMemberNode.setName("$STATEMENTS");
         irLoadFieldMemberNode.setName("$STATEMENTS");
         irLoadFieldMemberNode.setStatic(true);
         irLoadFieldMemberNode.setStatic(true);
 
 
@@ -278,7 +279,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
                 irBlockNode.getStatementsNodes().add(0, irDeclarationNode);
                 irBlockNode.getStatementsNodes().add(0, irDeclarationNode);
 
 
                 InvokeCallMemberNode irInvokeCallMemberNode = new InvokeCallMemberNode(internalLocation);
                 InvokeCallMemberNode irInvokeCallMemberNode = new InvokeCallMemberNode(internalLocation);
-                irInvokeCallMemberNode.setExpressionType(irDeclarationNode.getDeclarationType());
+                irInvokeCallMemberNode.attachDecoration(new IRDExpressionType(irDeclarationNode.getDeclarationType()));
                 irInvokeCallMemberNode.setLocalFunction(new LocalFunction(
                 irInvokeCallMemberNode.setLocalFunction(new LocalFunction(
                         getMethod.getName(), returnType, Collections.emptyList(), true, false));
                         getMethod.getName(), returnType, Collections.emptyList(), true, false));
                 irDeclarationNode.setExpressionNode(irInvokeCallMemberNode);
                 irDeclarationNode.setExpressionNode(irInvokeCallMemberNode);
@@ -315,7 +316,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
             irBlockNode.addStatementNode(irReturnNode);
             irBlockNode.addStatementNode(irReturnNode);
 
 
             ConstantNode irConstantNode = new ConstantNode(internalLocation);
             ConstantNode irConstantNode = new ConstantNode(internalLocation);
-            irConstantNode.setExpressionType(boolean.class);
+            irConstantNode.attachDecoration(new IRDExpressionType(boolean.class));
             irConstantNode.setConstant(scriptScope.getUsedVariables().contains(name));
             irConstantNode.setConstant(scriptScope.getUsedVariables().contains(name));
 
 
             irReturnNode.setExpressionNode(irConstantNode);
             irReturnNode.setExpressionNode(irConstantNode);
@@ -355,7 +356,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
             irCatchBlockNode.addStatementNode(irThrowNode);
             irCatchBlockNode.addStatementNode(irThrowNode);
 
 
             InvokeCallMemberNode irInvokeCallMemberNode = new InvokeCallMemberNode(internalLocation);
             InvokeCallMemberNode irInvokeCallMemberNode = new InvokeCallMemberNode(internalLocation);
-            irInvokeCallMemberNode.setExpressionType(ScriptException.class);
+            irInvokeCallMemberNode.attachDecoration(new IRDExpressionType(ScriptException.class));
             irInvokeCallMemberNode.setLocalFunction(
             irInvokeCallMemberNode.setLocalFunction(
                     new LocalFunction(
                     new LocalFunction(
                             "convertToScriptException",
                             "convertToScriptException",
@@ -369,24 +370,24 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
             irThrowNode.setExpressionNode(irInvokeCallMemberNode);
             irThrowNode.setExpressionNode(irInvokeCallMemberNode);
 
 
             LoadVariableNode irLoadVariableNode = new LoadVariableNode(internalLocation);
             LoadVariableNode irLoadVariableNode = new LoadVariableNode(internalLocation);
-            irLoadVariableNode.setExpressionType(ScriptException.class);
+            irLoadVariableNode.attachDecoration(new IRDExpressionType(ScriptException.class));
             irLoadVariableNode.setName("#painlessExplainError");
             irLoadVariableNode.setName("#painlessExplainError");
 
 
             irInvokeCallMemberNode.addArgumentNode(irLoadVariableNode);
             irInvokeCallMemberNode.addArgumentNode(irLoadVariableNode);
 
 
             BinaryImplNode irBinaryImplNode = new BinaryImplNode(internalLocation);
             BinaryImplNode irBinaryImplNode = new BinaryImplNode(internalLocation);
-            irBinaryImplNode.setExpressionType(Map.class);
+            irBinaryImplNode.attachDecoration(new IRDExpressionType(Map.class));
 
 
             irInvokeCallMemberNode.addArgumentNode(irBinaryImplNode);
             irInvokeCallMemberNode.addArgumentNode(irBinaryImplNode);
 
 
             irLoadVariableNode = new LoadVariableNode(internalLocation);
             irLoadVariableNode = new LoadVariableNode(internalLocation);
-            irLoadVariableNode.setExpressionType(PainlessExplainError.class);
+            irLoadVariableNode.attachDecoration(new IRDExpressionType(PainlessExplainError.class));
             irLoadVariableNode.setName("#painlessExplainError");
             irLoadVariableNode.setName("#painlessExplainError");
 
 
             irBinaryImplNode.setLeftNode(irLoadVariableNode);
             irBinaryImplNode.setLeftNode(irLoadVariableNode);
 
 
             InvokeCallNode irInvokeCallNode = new InvokeCallNode(internalLocation);
             InvokeCallNode irInvokeCallNode = new InvokeCallNode(internalLocation);
-            irInvokeCallNode.setExpressionType(Map.class);
+            irInvokeCallNode.attachDecoration(new IRDExpressionType(Map.class));
             irInvokeCallNode.setBox(PainlessExplainError.class);
             irInvokeCallNode.setBox(PainlessExplainError.class);
             irInvokeCallNode.setMethod(
             irInvokeCallNode.setMethod(
                     new PainlessMethod(
                     new PainlessMethod(
@@ -405,7 +406,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
             irBinaryImplNode.setRightNode(irInvokeCallNode);
             irBinaryImplNode.setRightNode(irInvokeCallNode);
 
 
             LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
             LoadFieldMemberNode irLoadFieldMemberNode = new LoadFieldMemberNode(internalLocation);
-            irLoadFieldMemberNode.setExpressionType(PainlessLookup.class);
+            irLoadFieldMemberNode.attachDecoration(new IRDExpressionType(PainlessLookup.class));
             irLoadFieldMemberNode.setName("$DEFINITION");
             irLoadFieldMemberNode.setName("$DEFINITION");
             irLoadFieldMemberNode.setStatic(true);
             irLoadFieldMemberNode.setStatic(true);
 
 
@@ -433,7 +434,7 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
                 irCatchBlockNode.addStatementNode(irThrowNode);
                 irCatchBlockNode.addStatementNode(irThrowNode);
 
 
                 irInvokeCallMemberNode = new InvokeCallMemberNode(internalLocation);
                 irInvokeCallMemberNode = new InvokeCallMemberNode(internalLocation);
-                irInvokeCallMemberNode.setExpressionType(ScriptException.class);
+                irInvokeCallMemberNode.attachDecoration(new IRDExpressionType(ScriptException.class));
                 irInvokeCallMemberNode.setLocalFunction(
                 irInvokeCallMemberNode.setLocalFunction(
                         new LocalFunction(
                         new LocalFunction(
                                 "convertToScriptException",
                                 "convertToScriptException",
@@ -447,23 +448,23 @@ public class PainlessUserTreeToIRTreePhase extends DefaultUserTreeToIRTreePhase
                 irThrowNode.setExpressionNode(irInvokeCallMemberNode);
                 irThrowNode.setExpressionNode(irInvokeCallMemberNode);
 
 
                 irLoadVariableNode = new LoadVariableNode(internalLocation);
                 irLoadVariableNode = new LoadVariableNode(internalLocation);
-                irLoadVariableNode.setExpressionType(ScriptException.class);
+                irLoadVariableNode.attachDecoration(new IRDExpressionType(ScriptException.class));
                 irLoadVariableNode.setName(name);
                 irLoadVariableNode.setName(name);
 
 
                 irInvokeCallMemberNode.addArgumentNode(irLoadVariableNode);
                 irInvokeCallMemberNode.addArgumentNode(irLoadVariableNode);
 
 
                 irBinaryImplNode = new BinaryImplNode(internalLocation);
                 irBinaryImplNode = new BinaryImplNode(internalLocation);
-                irBinaryImplNode.setExpressionType(Map.class);
+                irBinaryImplNode.attachDecoration(new IRDExpressionType(Map.class));
 
 
                 irInvokeCallMemberNode.addArgumentNode(irBinaryImplNode);
                 irInvokeCallMemberNode.addArgumentNode(irBinaryImplNode);
 
 
                 StaticNode irStaticNode = new StaticNode(internalLocation);
                 StaticNode irStaticNode = new StaticNode(internalLocation);
-                irStaticNode.setExpressionType(Collections.class);
+                irStaticNode.attachDecoration(new IRDExpressionType(Collections.class));
 
 
                 irBinaryImplNode.setLeftNode(irStaticNode);
                 irBinaryImplNode.setLeftNode(irStaticNode);
 
 
                 irInvokeCallNode = new InvokeCallNode(internalLocation);
                 irInvokeCallNode = new InvokeCallNode(internalLocation);
-                irInvokeCallNode.setExpressionType(Map.class);
+                irInvokeCallNode.attachDecoration(new IRDExpressionType(Map.class));
                 irInvokeCallNode.setBox(Collections.class);
                 irInvokeCallNode.setBox(Collections.class);
                 irInvokeCallNode.setMethod(
                 irInvokeCallNode.setMethod(
                         new PainlessMethod(
                         new PainlessMethod(

+ 52 - 0
modules/lang-painless/src/main/java/org/elasticsearch/painless/symbol/IRDecorations.java

@@ -0,0 +1,52 @@
+/*
+ * 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.symbol;
+
+import org.elasticsearch.painless.ir.IRNode.IRDecoration;
+import org.elasticsearch.painless.lookup.PainlessLookupUtility;
+
+import java.util.Objects;
+
+public class IRDecorations {
+
+    public abstract static class IRDType implements IRDecoration {
+
+        private final Class<?> type;
+
+        public IRDType(Class<?> type) {
+            this.type = Objects.requireNonNull(type);
+        }
+
+        public Class<?> getType() {
+            return type;
+        }
+
+        public String getCanonicalTypeName() {
+            return PainlessLookupUtility.typeToCanonicalTypeName(type);
+        }
+    }
+
+    public static class IRDExpressionType extends IRDType {
+
+        public IRDExpressionType(Class<?> expressionType) {
+            super(expressionType);
+        }
+    }
+}