From ffcd659264be57b32287e56e3ee775ed49f7d87f Mon Sep 17 00:00:00 2001
From: Tyler Gregg <greggt@amazon.com>
Date: Fri, 17 Jan 2025 11:20:28 -0800
Subject: [PATCH] Adds an Expression pool to reduce allocations.

---
 .../impl/IonReaderContinuableCoreBinary.java  |   4 +-
 .../amazon/ion/impl/IonReaderTextSystemX.java |   2 +-
 .../ion/impl/macro/EExpressionArgsReader.java |  43 +-
 .../com/amazon/ion/impl/macro/Expression.kt   |  64 +--
 .../impl/macro/PooledExpressionFactory.java   | 380 ++++++++++++++++++
 5 files changed, 437 insertions(+), 56 deletions(-)
 create mode 100644 src/main/java/com/amazon/ion/impl/macro/PooledExpressionFactory.java

diff --git a/src/main/java/com/amazon/ion/impl/IonReaderContinuableCoreBinary.java b/src/main/java/com/amazon/ion/impl/IonReaderContinuableCoreBinary.java
index 69237ce5a..7a21c29bc 100644
--- a/src/main/java/com/amazon/ion/impl/IonReaderContinuableCoreBinary.java
+++ b/src/main/java/com/amazon/ion/impl/IonReaderContinuableCoreBinary.java
@@ -1647,7 +1647,7 @@ private void readGroupExpression(Macro.Parameter parameter, boolean requireSingl
             if (exitArgumentGroup() == Event.NEEDS_DATA) {
                 throw new UnsupportedOperationException("TODO: support continuable parsing of macro arguments.");
             }
-            expressions.set(startIndex, new Expression.ExpressionGroup(startIndex, expressions.size()));
+            expressions.set(startIndex, expressionPool.createExpressionGroup(startIndex, expressions.size()));
         }
 
         /**
@@ -1655,7 +1655,7 @@ private void readGroupExpression(Macro.Parameter parameter, boolean requireSingl
          */
         private void addVoidExpression() {
             int startIndex = expressions.size();
-            expressions.add(new Expression.ExpressionGroup(startIndex, startIndex + 1));
+            expressions.add(expressionPool.createExpressionGroup(startIndex, startIndex + 1));
         }
 
         @Override
diff --git a/src/main/java/com/amazon/ion/impl/IonReaderTextSystemX.java b/src/main/java/com/amazon/ion/impl/IonReaderTextSystemX.java
index f4144f5ce..37b722ef4 100644
--- a/src/main/java/com/amazon/ion/impl/IonReaderTextSystemX.java
+++ b/src/main/java/com/amazon/ion/impl/IonReaderTextSystemX.java
@@ -1255,7 +1255,7 @@ protected void readParameter(Macro.Parameter parameter, long parameterPresence,
             if (IonReaderTextSystemX.this.nextRaw() == null) {
                 // Add an empty expression group if nothing present.
                 int index = expressions.size() + 1;
-                expressions.add(new Expression.ExpressionGroup(index, index));
+                expressions.add(expressionPool.createExpressionGroup(index, index));
                 return;
             }
             readValueAsExpression(isTrailing && parameter.getCardinality().canBeMulti);
diff --git a/src/main/java/com/amazon/ion/impl/macro/EExpressionArgsReader.java b/src/main/java/com/amazon/ion/impl/macro/EExpressionArgsReader.java
index a3bc1296f..61ec22983 100644
--- a/src/main/java/com/amazon/ion/impl/macro/EExpressionArgsReader.java
+++ b/src/main/java/com/amazon/ion/impl/macro/EExpressionArgsReader.java
@@ -7,7 +7,6 @@
 import com.amazon.ion.impl.bin.PresenceBitmap;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 /**
@@ -29,6 +28,8 @@ public abstract class EExpressionArgsReader {
     // e-expression invocations.
     protected final List<Expression.EExpressionBodyExpression> expressions = new ArrayList<>(64);
 
+    protected final PooledExpressionFactory expressionPool = new PooledExpressionFactory();
+
     /**
      * Constructor.
      * @param reader the {@link ReaderAdapter} from which to read {@link Expression}s.
@@ -114,45 +115,45 @@ private void readScalarValueAsExpression(
     ) {
         Expression.EExpressionBodyExpression expression;
         if (reader.isNullValue()) {
-            expression = new Expression.NullValue(annotations, type);
+            expression = expressionPool.createNullValue(annotations, type);
         } else {
             switch (type) {
                 case BOOL:
-                    expression = new Expression.BoolValue(annotations, reader.booleanValue());
+                    expression = expressionPool.createBoolValue(annotations, reader.booleanValue());
                     break;
                 case INT:
                     switch (reader.getIntegerSize()) {
                         case INT:
                         case LONG:
-                            expression = new Expression.LongIntValue(annotations, reader.longValue());
+                            expression = expressionPool.createLongIntValue(annotations, reader.longValue());
                             break;
                         case BIG_INTEGER:
-                            expression = new Expression.BigIntValue(annotations, reader.bigIntegerValue());
+                            expression = expressionPool.createBigIntValue(annotations, reader.bigIntegerValue());
                             break;
                         default:
                             throw new IllegalStateException();
                     }
                     break;
                 case FLOAT:
-                    expression = new Expression.FloatValue(annotations, reader.doubleValue());
+                    expression = expressionPool.createFloatValue(annotations, reader.doubleValue());
                     break;
                 case DECIMAL:
-                    expression = new Expression.DecimalValue(annotations, reader.decimalValue());
+                    expression = expressionPool.createDecimalValue(annotations, reader.decimalValue());
                     break;
                 case TIMESTAMP:
-                    expression = new Expression.TimestampValue(annotations, reader.timestampValue());
+                    expression = expressionPool.createTimestampValue(annotations, reader.timestampValue());
                     break;
                 case SYMBOL:
-                    expression = new Expression.SymbolValue(annotations, reader.symbolValue());
+                    expression = expressionPool.createSymbolValue(annotations, reader.symbolValue());
                     break;
                 case STRING:
-                    expression = new Expression.StringValue(annotations, reader.stringValue());
+                    expression = expressionPool.createStringValue(annotations, reader.stringValue());
                     break;
                 case CLOB:
-                    expression = new Expression.ClobValue(annotations, reader.newBytes());
+                    expression = expressionPool.createClobValue(annotations, reader.newBytes());
                     break;
                 case BLOB:
-                    expression = new Expression.BlobValue(annotations, reader.newBytes());
+                    expression = expressionPool.createBlobValue(annotations, reader.newBytes());
                     break;
                 default:
                     throw new IllegalStateException();
@@ -178,7 +179,7 @@ private void readContainerValueAsExpression(
         stepInRaw();
         while (nextRaw()) {
             if (type == IonType.STRUCT) {
-                expressions.add(new Expression.FieldName(reader.getFieldNameSymbol()));
+                expressions.add(expressionPool.createFieldName(reader.getFieldNameSymbol()));
             }
             readValueAsExpression(false); // TODO avoid recursion
         }
@@ -187,18 +188,17 @@ private void readContainerValueAsExpression(
         // start and end indices of its expressions.
         Expression.EExpressionBodyExpression expression;
         if (isExpressionGroup) {
-            expression =  new Expression.ExpressionGroup(startIndex, expressions.size());
+            expression =  expressionPool.createExpressionGroup(startIndex, expressions.size());
         } else {
             switch (type) {
                 case LIST:
-                    expression = new Expression.ListValue(annotations, startIndex, expressions.size());
+                    expression = expressionPool.createListValue(annotations, startIndex, expressions.size());
                     break;
                 case SEXP:
-                    expression = new Expression.SExpValue(annotations, startIndex, expressions.size());
+                    expression = expressionPool.createSExpValue(annotations, startIndex, expressions.size());
                     break;
                 case STRUCT:
-                    // TODO consider whether templateStructIndex could be leveraged or should be removed
-                    expression = new Expression.StructValue(annotations, startIndex, expressions.size(), Collections.emptyMap());
+                    expression = expressionPool.createStructValue(annotations, startIndex, expressions.size());
                     break;
                 default:
                     throw new IllegalStateException();
@@ -216,7 +216,7 @@ private void readStreamAsExpressionGroup() {
         do {
             readValueAsExpression(false); // TODO avoid recursion
         } while (nextRaw());
-        expressions.set(startIndex, new Expression.ExpressionGroup(startIndex, expressions.size()));
+        expressions.set(startIndex, expressionPool.createExpressionGroup(startIndex, expressions.size()));
     }
 
     /**
@@ -261,7 +261,7 @@ private void collectEExpressionArgs() {
             );
         }
         stepOutOfEExpression();
-        expressions.set(invocationStartIndex, new Expression.EExpression(macro, invocationStartIndex, expressions.size()));
+        expressions.set(invocationStartIndex, expressionPool.createEExpression(macro, invocationStartIndex, expressions.size()));
     }
 
     /**
@@ -270,9 +270,10 @@ private void collectEExpressionArgs() {
      */
     public void beginEvaluatingMacroInvocation(MacroEvaluator macroEvaluator) {
         expressions.clear();
+        expressionPool.clear();
         // TODO performance: avoid fully materializing all expressions up-front.
         if (reader.isInStruct()) {
-            expressions.add(new Expression.FieldName(reader.getFieldNameSymbol()));
+            expressions.add(expressionPool.createFieldName(reader.getFieldNameSymbol()));
         }
         collectEExpressionArgs();
         macroEvaluator.initExpansion(expressions);
diff --git a/src/main/java/com/amazon/ion/impl/macro/Expression.kt b/src/main/java/com/amazon/ion/impl/macro/Expression.kt
index fc5f983a5..ff95cd260 100644
--- a/src/main/java/com/amazon/ion/impl/macro/Expression.kt
+++ b/src/main/java/com/amazon/ion/impl/macro/Expression.kt
@@ -28,7 +28,7 @@ sealed interface Expression {
          * The position of this expression in its containing list.
          * Child expressions (if any) start at `selfIndex + 1`.
          */
-        val selfIndex: Int
+        var selfIndex: Int
         /**
          * The index of the first child expression (if any).
          * Always equal to `selfIndex + 1`.
@@ -38,7 +38,7 @@ sealed interface Expression {
          * The exclusive end of the child expressions (if any).
          * If there are no child expressions, will be equal to [startInclusive].
          */
-        val endExclusive: Int
+        var endExclusive: Int
     }
 
     /** Marker interface representing expressions that can be present in E-Expressions. */
@@ -72,7 +72,7 @@ sealed interface Expression {
      * Interface for expressions that are _values_ in the Ion data model.
      */
     sealed interface DataModelValue : DataModelExpression {
-        val annotations: List<SymbolToken>
+        var annotations: List<SymbolToken>
         val type: IonType
 
         fun withAnnotations(annotations: List<SymbolToken>): DataModelValue
@@ -97,14 +97,14 @@ sealed interface Expression {
      * @property selfIndex the index of the first expression of the expression group (i.e. this instance)
      * @property endExclusive the index of the last expression contained in the expression group
      */
-    data class ExpressionGroup(override val selfIndex: Int, override val endExclusive: Int) : EExpressionBodyExpression, TemplateBodyExpression, HasStartAndEnd
+    data class ExpressionGroup(override var selfIndex: Int, override var endExclusive: Int) : EExpressionBodyExpression, TemplateBodyExpression, HasStartAndEnd
 
     // Scalars
-    data class NullValue(override val annotations: List<SymbolToken> = emptyList(), override val type: IonType) : DataModelValue {
+    data class NullValue(override var annotations: List<SymbolToken> = emptyList(), override var type: IonType) : DataModelValue {
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
     }
 
-    data class BoolValue(override val annotations: List<SymbolToken> = emptyList(), val value: Boolean) : DataModelValue {
+    data class BoolValue(override var annotations: List<SymbolToken> = emptyList(), var value: Boolean) : DataModelValue {
         override val type: IonType get() = IonType.BOOL
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
     }
@@ -114,31 +114,31 @@ sealed interface Expression {
         val longValue: Long
     }
 
-    data class LongIntValue(override val annotations: List<SymbolToken> = emptyList(), val value: Long) : IntValue {
+    data class LongIntValue(override var annotations: List<SymbolToken> = emptyList(), var value: Long) : IntValue {
         override val type: IonType get() = IonType.INT
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
         override val bigIntegerValue: BigInteger get() = BigInteger.valueOf(value)
         override val longValue: Long get() = value
     }
 
-    data class BigIntValue(override val annotations: List<SymbolToken> = emptyList(), val value: BigInteger) : IntValue {
+    data class BigIntValue(override var annotations: List<SymbolToken> = emptyList(), var value: BigInteger) : IntValue {
         override val type: IonType get() = IonType.INT
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
         override val bigIntegerValue: BigInteger get() = value
         override val longValue: Long get() = value.longValueExact()
     }
 
-    data class FloatValue(override val annotations: List<SymbolToken> = emptyList(), val value: Double) : DataModelValue {
+    data class FloatValue(override var annotations: List<SymbolToken> = emptyList(), var value: Double) : DataModelValue {
         override val type: IonType get() = IonType.FLOAT
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
     }
 
-    data class DecimalValue(override val annotations: List<SymbolToken> = emptyList(), val value: BigDecimal) : DataModelValue {
+    data class DecimalValue(override var annotations: List<SymbolToken> = emptyList(), var value: BigDecimal) : DataModelValue {
         override val type: IonType get() = IonType.DECIMAL
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
     }
 
-    data class TimestampValue(override val annotations: List<SymbolToken> = emptyList(), val value: Timestamp) : DataModelValue {
+    data class TimestampValue(override var annotations: List<SymbolToken> = emptyList(), var value: Timestamp) : DataModelValue {
         override val type: IonType get() = IonType.TIMESTAMP
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
     }
@@ -147,13 +147,13 @@ sealed interface Expression {
         val stringValue: String
     }
 
-    data class StringValue(override val annotations: List<SymbolToken> = emptyList(), val value: String) : TextValue {
+    data class StringValue(override var annotations: List<SymbolToken> = emptyList(), var value: String) : TextValue {
         override val type: IonType get() = IonType.STRING
         override val stringValue: String get() = value
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
     }
 
-    data class SymbolValue(override val annotations: List<SymbolToken> = emptyList(), val value: SymbolToken) : TextValue {
+    data class SymbolValue(override var annotations: List<SymbolToken> = emptyList(), var value: SymbolToken) : TextValue {
         override val type: IonType get() = IonType.SYMBOL
         override val stringValue: String get() = value.assumeText()
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
@@ -166,7 +166,7 @@ sealed interface Expression {
     }
 
     // We must override hashcode and equals in the lob types because `value` is a `byte[]`
-    data class BlobValue(override val annotations: List<SymbolToken> = emptyList(), override val value: ByteArray) : LobValue {
+    data class BlobValue(override var annotations: List<SymbolToken> = emptyList(), override var value: ByteArray) : LobValue {
         override val type: IonType get() = IonType.BLOB
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
         override fun hashCode(): Int = annotations.hashCode() * 31 + value.contentHashCode()
@@ -178,7 +178,7 @@ sealed interface Expression {
         }
     }
 
-    data class ClobValue(override val annotations: List<SymbolToken> = emptyList(), override val value: ByteArray) : LobValue {
+    data class ClobValue(override var annotations: List<SymbolToken> = emptyList(), override var value: ByteArray) : LobValue {
         override val type: IonType get() = IonType.CLOB
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
         override fun hashCode(): Int = annotations.hashCode() * 31 + value.contentHashCode()
@@ -197,9 +197,9 @@ sealed interface Expression {
      * @property endExclusive the index of the last expression contained in the list
      */
     data class ListValue(
-        override val annotations: List<SymbolToken> = emptyList(),
-        override val selfIndex: Int,
-        override val endExclusive: Int
+        override var annotations: List<SymbolToken> = emptyList(),
+        override var selfIndex: Int,
+        override var endExclusive: Int
     ) : DataModelContainer {
         override val type: IonType get() = IonType.LIST
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
@@ -209,9 +209,9 @@ sealed interface Expression {
      * An Ion SExp that could contain variables or macro invocations.
      */
     data class SExpValue(
-        override val annotations: List<SymbolToken> = emptyList(),
-        override val selfIndex: Int,
-        override val endExclusive: Int
+        override var annotations: List<SymbolToken> = emptyList(),
+        override var selfIndex: Int,
+        override var endExclusive: Int
     ) : DataModelContainer {
         override val type: IonType get() = IonType.SEXP
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
@@ -221,21 +221,21 @@ sealed interface Expression {
      * An Ion Struct that could contain variables or macro invocations.
      */
     data class StructValue(
-        override val annotations: List<SymbolToken> = emptyList(),
-        override val selfIndex: Int,
-        override val endExclusive: Int,
+        override var annotations: List<SymbolToken> = emptyList(),
+        override var selfIndex: Int,
+        override var endExclusive: Int,
         val templateStructIndex: Map<String, List<Int>>
     ) : DataModelContainer {
         override val type: IonType get() = IonType.STRUCT
         override fun withAnnotations(annotations: List<SymbolToken>) = copy(annotations = annotations)
     }
 
-    data class FieldName(val value: SymbolToken) : DataModelExpression
+    data class FieldName(var value: SymbolToken) : DataModelExpression
 
     /**
      * A reference to a variable that needs to be expanded.
      */
-    data class VariableRef(val signatureIndex: Int) : TemplateBodyExpression
+    data class VariableRef(var signatureIndex: Int) : TemplateBodyExpression
 
     sealed interface InvokableExpression : HasStartAndEnd, Expression {
         val macro: Macro
@@ -245,17 +245,17 @@ sealed interface Expression {
      * A macro invocation that needs to be expanded.
      */
     data class MacroInvocation(
-        override val macro: Macro,
-        override val selfIndex: Int,
-        override val endExclusive: Int
+        override var macro: Macro,
+        override var selfIndex: Int,
+        override var endExclusive: Int
     ) : TemplateBodyExpression, HasStartAndEnd, InvokableExpression
 
     /**
      * An e-expression that needs to be expanded.
      */
     data class EExpression(
-        override val macro: Macro,
-        override val selfIndex: Int,
-        override val endExclusive: Int
+        override var macro: Macro,
+        override var selfIndex: Int,
+        override var endExclusive: Int
     ) : EExpressionBodyExpression, HasStartAndEnd, InvokableExpression
 }
diff --git a/src/main/java/com/amazon/ion/impl/macro/PooledExpressionFactory.java b/src/main/java/com/amazon/ion/impl/macro/PooledExpressionFactory.java
new file mode 100644
index 000000000..e48bd13e9
--- /dev/null
+++ b/src/main/java/com/amazon/ion/impl/macro/PooledExpressionFactory.java
@@ -0,0 +1,380 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+package com.amazon.ion.impl.macro;
+
+import com.amazon.ion.IonType;
+import com.amazon.ion.SymbolToken;
+import com.amazon.ion.Timestamp;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A factory for {@link Expression} instances. Avoids repetitive allocations by pooling instances. {@link #clear()}
+ * returns all instances to the pool. Note: this should only be used when the lifetime of the provided instances
+ * is known, and that lifetime must be the same for all instances provided between calls to {@link #clear()}.
+ */
+public class PooledExpressionFactory {
+
+    private static final int POOL_SIZE = 32;
+
+    private Expression.NullValue[] nullValues = new Expression.NullValue[POOL_SIZE];
+    private Expression.BoolValue[] boolValues = new Expression.BoolValue[POOL_SIZE];
+    private Expression.LongIntValue[] longIntValues = new Expression.LongIntValue[POOL_SIZE];
+    private Expression.BigIntValue[] bigIntValues = new Expression.BigIntValue[POOL_SIZE];
+    private Expression.FloatValue[] floatValues = new Expression.FloatValue[POOL_SIZE];
+    private Expression.DecimalValue[] decimalValues = new Expression.DecimalValue[POOL_SIZE];
+    private Expression.TimestampValue[] timestampValues = new Expression.TimestampValue[POOL_SIZE];
+    private Expression.SymbolValue[] symbolValues = new Expression.SymbolValue[POOL_SIZE];
+    private Expression.StringValue[] stringValues = new Expression.StringValue[POOL_SIZE];
+    private Expression.ClobValue[] clobValues = new Expression.ClobValue[POOL_SIZE];
+    private Expression.BlobValue[] blobValues = new Expression.BlobValue[POOL_SIZE];
+    private Expression.FieldName[] fieldNames = new Expression.FieldName[POOL_SIZE];
+    private Expression.EExpression[] eExpressions = new Expression.EExpression[POOL_SIZE];
+    private Expression.ExpressionGroup[] expressionGroups = new Expression.ExpressionGroup[POOL_SIZE];
+    private Expression.ListValue[] listValues = new Expression.ListValue[POOL_SIZE];
+    private Expression.StructValue[] structValues = new Expression.StructValue[POOL_SIZE];
+    private Expression.SExpValue[] sexpValues = new Expression.SExpValue[POOL_SIZE];
+
+    private int nullValuesIndex = 0;
+    private int boolValuesIndex = 0;
+    private int longIntValuesIndex = 0;
+    private int bigIntValuesIndex = 0;
+    private int floatValuesIndex = 0;
+    private int decimalValuesIndex = 0;
+    private int timestampValuesIndex = 0;
+    private int symbolValuesIndex = 0;
+    private int stringValuesIndex = 0;
+    private int clobValuesIndex = 0;
+    private int blobValuesIndex = 0;
+    private int fieldNamesIndex = 0;
+    private int eExpressionsIndex = 0;
+    private int expressionGroupsIndex = 0;
+    private int listValuesIndex = 0;
+    private int structValuesIndex = 0;
+    private int sexpValuesIndex = 0;
+
+    private <T> T[] grow(T[] array) {
+        return Arrays.copyOf(array, array.length * 2);
+    }
+
+    public Expression.NullValue createNullValue(List<SymbolToken> annotations, IonType type) {
+        Expression.NullValue expression;
+        if (nullValuesIndex >= nullValues.length) {
+            nullValues = grow(nullValues);
+        }
+        expression = nullValues[nullValuesIndex];
+        if (expression == null) {
+            expression = new Expression.NullValue(annotations, type);
+            nullValues[nullValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setType(type);
+        }
+        nullValuesIndex++;
+        return expression;
+    }
+
+    public Expression.BoolValue createBoolValue(List<SymbolToken> annotations, boolean value) {
+        Expression.BoolValue expression;
+        if (boolValuesIndex >= boolValues.length) {
+            boolValues = grow(boolValues);
+        }
+        expression = boolValues[boolValuesIndex];
+        if (expression == null) {
+            expression = new Expression.BoolValue(annotations, value);
+            boolValues[boolValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setValue(value);
+        }
+        boolValuesIndex++;
+        return expression;
+    }
+
+    public Expression.LongIntValue createLongIntValue(List<SymbolToken> annotations, long value) {
+        Expression.LongIntValue expression;
+        if (longIntValuesIndex >= longIntValues.length) {
+            longIntValues = grow(longIntValues);
+        }
+        expression = longIntValues[longIntValuesIndex];
+        if (expression == null) {
+            expression = new Expression.LongIntValue(annotations, value);
+            longIntValues[longIntValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setValue(value);
+        }
+        longIntValuesIndex++;
+        return expression;
+    }
+
+    public Expression.BigIntValue createBigIntValue(List<SymbolToken> annotations, BigInteger value) {
+        Expression.BigIntValue expression;
+        if (bigIntValuesIndex >= bigIntValues.length) {
+            bigIntValues = grow(bigIntValues);
+        }
+        expression = bigIntValues[bigIntValuesIndex];
+        if (expression == null) {
+            expression = new Expression.BigIntValue(annotations, value);
+            bigIntValues[bigIntValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setValue(value);
+        }
+        bigIntValuesIndex++;
+        return expression;
+    }
+
+
+    public Expression.FloatValue createFloatValue(List<SymbolToken> annotations, double value) {
+        Expression.FloatValue expression;
+        if (floatValuesIndex >= floatValues.length) {
+            floatValues = grow(floatValues);
+        }
+        expression = floatValues[floatValuesIndex];
+        if (expression == null) {
+            expression = new Expression.FloatValue(annotations, value);
+            floatValues[floatValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setValue(value);
+        }
+        floatValuesIndex++;
+        return expression;
+    }
+
+    public Expression.DecimalValue createDecimalValue(List<SymbolToken> annotations, BigDecimal value) {
+        Expression.DecimalValue expression;
+        if (decimalValuesIndex >= decimalValues.length) {
+            decimalValues = grow(decimalValues);
+        }
+        expression = decimalValues[decimalValuesIndex];
+        if (expression == null) {
+            expression = new Expression.DecimalValue(annotations, value);
+            decimalValues[decimalValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setValue(value);
+        }
+        decimalValuesIndex++;
+        return expression;
+    }
+
+    public Expression.TimestampValue createTimestampValue(List<SymbolToken> annotations, Timestamp value) {
+        Expression.TimestampValue expression;
+        if (timestampValuesIndex >= timestampValues.length) {
+            timestampValues = grow(timestampValues);
+        }
+        expression = timestampValues[timestampValuesIndex];
+        if (expression == null) {
+            expression = new Expression.TimestampValue(annotations, value);
+            timestampValues[timestampValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setValue(value);
+        }
+        timestampValuesIndex++;
+        return expression;
+    }
+
+    public Expression.SymbolValue createSymbolValue(List<SymbolToken> annotations, SymbolToken value) {
+        Expression.SymbolValue expression;
+        if (symbolValuesIndex >= symbolValues.length) {
+            symbolValues = grow(symbolValues);
+        }
+        expression = symbolValues[symbolValuesIndex];
+        if (expression == null) {
+            expression = new Expression.SymbolValue(annotations, value);
+            symbolValues[symbolValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setValue(value);
+        }
+        symbolValuesIndex++;
+        return expression;
+    }
+
+    public Expression.StringValue createStringValue(List<SymbolToken> annotations, String value) {
+        Expression.StringValue expression;
+        if (stringValuesIndex >= stringValues.length) {
+            stringValues = grow(stringValues);
+        }
+        expression = stringValues[stringValuesIndex];
+        if (expression == null) {
+            expression = new Expression.StringValue(annotations, value);
+            stringValues[stringValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setValue(value);
+        }
+        stringValuesIndex++;
+        return expression;
+    }
+
+    public Expression.ClobValue createClobValue(List<SymbolToken> annotations, byte[] value) {
+        Expression.ClobValue expression;
+        if (clobValuesIndex >= clobValues.length) {
+            clobValues = grow(clobValues);
+        }
+        expression = clobValues[clobValuesIndex];
+        if (expression == null) {
+            expression = new Expression.ClobValue(annotations, value);
+            clobValues[clobValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setValue(value);
+        }
+        clobValuesIndex++;
+        return expression;
+    }
+
+    public Expression.BlobValue createBlobValue(List<SymbolToken> annotations, byte[] value) {
+        Expression.BlobValue expression;
+        if (blobValuesIndex >= blobValues.length) {
+            blobValues = grow(blobValues);
+        }
+        expression = blobValues[blobValuesIndex];
+        if (expression == null) {
+            expression = new Expression.BlobValue(annotations, value);
+            blobValues[blobValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setValue(value);
+        }
+        blobValuesIndex++;
+        return expression;
+    }
+
+    public Expression.FieldName createFieldName(SymbolToken name) {
+        Expression.FieldName expression;
+        if (fieldNamesIndex >= fieldNames.length) {
+            fieldNames = grow(fieldNames);
+        }
+        expression = fieldNames[fieldNamesIndex];
+        if (expression == null) {
+            expression = new Expression.FieldName(name);
+            fieldNames[fieldNamesIndex] = expression;
+        } else {
+            expression.setValue(name);
+        }
+        fieldNamesIndex++;
+        return expression;
+    }
+
+    public Expression.EExpression createEExpression(Macro macro, int selfIndex, int endExclusive) {
+        Expression.EExpression expression;
+        if (eExpressionsIndex >= eExpressions.length) {
+            eExpressions = grow(eExpressions);
+        }
+        expression = eExpressions[eExpressionsIndex];
+        if (expression == null) {
+            expression = new Expression.EExpression(macro, selfIndex, endExclusive);
+            eExpressions[eExpressionsIndex] = expression;
+        } else {
+            expression.setMacro(macro);
+            expression.setSelfIndex(selfIndex);
+            expression.setEndExclusive(endExclusive);
+        }
+        eExpressionsIndex++;
+        return expression;
+    }
+
+    public Expression.ExpressionGroup createExpressionGroup(int selfIndex, int endExclusive) {
+        Expression.ExpressionGroup expression;
+        if (expressionGroupsIndex >= expressionGroups.length) {
+            expressionGroups = grow(expressionGroups);
+        }
+        expression = expressionGroups[expressionGroupsIndex];
+        if (expression == null) {
+            expression = new Expression.ExpressionGroup(selfIndex, endExclusive);
+            expressionGroups[expressionGroupsIndex] = expression;
+        } else {
+            expression.setSelfIndex(selfIndex);
+            expression.setEndExclusive(endExclusive);
+        }
+        expressionGroupsIndex++;
+        return expression;
+    }
+
+    public Expression.ListValue createListValue(List<SymbolToken> annotations, int selfIndex, int endExclusive) {
+        Expression.ListValue expression;
+        if (listValuesIndex >= listValues.length) {
+            listValues = grow(listValues);
+        }
+        expression = listValues[listValuesIndex];
+        if (expression == null) {
+            expression = new Expression.ListValue(annotations, selfIndex, endExclusive);
+            listValues[listValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setSelfIndex(selfIndex);
+            expression.setEndExclusive(endExclusive);
+        }
+        listValuesIndex++;
+        return expression;
+    }
+
+    public Expression.StructValue createStructValue(List<SymbolToken> annotations, int selfIndex, int endExclusive) {
+        Expression.StructValue expression;
+        if (structValuesIndex >= structValues.length) {
+            structValues = grow(structValues);
+        }
+        expression = structValues[structValuesIndex];
+        if (expression == null) {
+            // TODO consider whether templateStructIndex could be leveraged or should be removed
+            expression = new Expression.StructValue(annotations, selfIndex, endExclusive, Collections.emptyMap());
+            structValues[structValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setSelfIndex(selfIndex);
+            expression.setEndExclusive(endExclusive);
+        }
+        structValuesIndex++;
+        return expression;
+    }
+
+    public Expression.SExpValue createSExpValue(List<SymbolToken> annotations, int selfIndex, int endExclusive) {
+        Expression.SExpValue expression;
+        if (sexpValuesIndex >= sexpValues.length) {
+            sexpValues = grow(sexpValues);
+        }
+        expression = sexpValues[sexpValuesIndex];
+        if (expression == null) {
+            expression = new Expression.SExpValue(annotations, selfIndex, endExclusive);
+            sexpValues[sexpValuesIndex] = expression;
+        } else {
+            expression.setAnnotations(annotations);
+            expression.setSelfIndex(selfIndex);
+            expression.setEndExclusive(endExclusive);
+        }
+        sexpValuesIndex++;
+        return expression;
+    }
+
+    /**
+     * Returns all instances to the pool.
+     */
+    public void clear() {
+        nullValuesIndex = 0;
+        boolValuesIndex = 0;
+        longIntValuesIndex = 0;
+        bigIntValuesIndex = 0;
+        floatValuesIndex = 0;
+        decimalValuesIndex = 0;
+        timestampValuesIndex = 0;
+        symbolValuesIndex = 0;
+        stringValuesIndex = 0;
+        clobValuesIndex = 0;
+        blobValuesIndex = 0;
+        fieldNamesIndex = 0;
+        eExpressionsIndex = 0;
+        expressionGroupsIndex = 0;
+        listValuesIndex = 0;
+        structValuesIndex = 0;
+        sexpValuesIndex = 0;
+    }
+}