ConstantPoolGen.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF 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.apache.bcel.generic;
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.Map;
- import org.apache.bcel.Const;
- import org.apache.bcel.classfile.Constant;
- import org.apache.bcel.classfile.ConstantCP;
- import org.apache.bcel.classfile.ConstantClass;
- import org.apache.bcel.classfile.ConstantDouble;
- import org.apache.bcel.classfile.ConstantDynamic;
- import org.apache.bcel.classfile.ConstantFieldref;
- import org.apache.bcel.classfile.ConstantFloat;
- import org.apache.bcel.classfile.ConstantInteger;
- import org.apache.bcel.classfile.ConstantInterfaceMethodref;
- import org.apache.bcel.classfile.ConstantInvokeDynamic;
- import org.apache.bcel.classfile.ConstantLong;
- import org.apache.bcel.classfile.ConstantMethodref;
- import org.apache.bcel.classfile.ConstantNameAndType;
- import org.apache.bcel.classfile.ConstantPool;
- import org.apache.bcel.classfile.ConstantString;
- import org.apache.bcel.classfile.ConstantUtf8;
- import org.apache.bcel.classfile.Utility;
- /**
- * This class is used to build up a constant pool. The user adds constants via 'addXXX' methods, 'addString',
- * 'addClass', etc.. These methods return an index into the constant pool. Finally, 'getFinalConstantPool()' returns the
- * constant pool built up. Intermediate versions of the constant pool can be obtained with 'getConstantPool()'. A
- * constant pool has capacity for Constants.MAX_SHORT entries. Note that the first (0) is used by the JVM and that
- * Double and Long constants need two slots.
- *
- * @see Constant
- */
- public class ConstantPoolGen {
- private static final int DEFAULT_BUFFER_SIZE = 256;
- private static final String METHODREF_DELIM = ":";
- private static final String IMETHODREF_DELIM = "#";
- private static final String FIELDREF_DELIM = "&";
- private static final String NAT_DELIM = "%"; // Name and Type
- /**
- * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
- */
- @Deprecated
- protected int size;
- /**
- * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
- */
- @Deprecated
- protected Constant[] constants;
- /**
- * @deprecated (since 6.0) will be made private; do not access directly, use getSize()
- */
- @Deprecated
- protected int index = 1; // First entry (0) used by JVM
- private final Map<String, Integer> stringTable = new HashMap<>();
- private final Map<String, Integer> classTable = new HashMap<>();
- private final Map<String, Integer> utf8Table = new HashMap<>();
- private final Map<String, Integer> natTable = new HashMap<>();
- private final Map<String, Integer> cpTable = new HashMap<>();
- /**
- * Constructs a new empty constant pool.
- */
- public ConstantPoolGen() {
- size = DEFAULT_BUFFER_SIZE;
- constants = new Constant[size];
- }
- /**
- * Constructs a new instance with the given array of constants.
- *
- * @param cs array of given constants, new ones will be appended
- */
- public ConstantPoolGen(final Constant[] cs) {
- final StringBuilder sb = new StringBuilder(DEFAULT_BUFFER_SIZE);
- size = Math.min(Math.max(DEFAULT_BUFFER_SIZE, cs.length + 64), Const.MAX_CP_ENTRIES + 1);
- constants = Arrays.copyOf(cs, size);
- if (cs.length > 0) {
- index = cs.length;
- }
- for (int i = 1; i < index; i++) {
- final Constant c = constants[i];
- if (c instanceof ConstantString) {
- final ConstantString s = (ConstantString) c;
- final ConstantUtf8 u8 = (ConstantUtf8) constants[s.getStringIndex()];
- final String key = u8.getBytes();
- if (!stringTable.containsKey(key)) {
- stringTable.put(key, Integer.valueOf(i));
- }
- } else if (c instanceof ConstantClass) {
- final ConstantClass s = (ConstantClass) c;
- final ConstantUtf8 u8 = (ConstantUtf8) constants[s.getNameIndex()];
- final String key = u8.getBytes();
- if (!classTable.containsKey(key)) {
- classTable.put(key, Integer.valueOf(i));
- }
- } else if (c instanceof ConstantNameAndType) {
- final ConstantNameAndType n = (ConstantNameAndType) c;
- final ConstantUtf8 u8NameIdx = (ConstantUtf8) constants[n.getNameIndex()];
- final ConstantUtf8 u8SigIdx = (ConstantUtf8) constants[n.getSignatureIndex()];
- sb.append(u8NameIdx.getBytes());
- sb.append(NAT_DELIM);
- sb.append(u8SigIdx.getBytes());
- final String key = sb.toString();
- sb.delete(0, sb.length());
- if (!natTable.containsKey(key)) {
- natTable.put(key, Integer.valueOf(i));
- }
- } else if (c instanceof ConstantUtf8) {
- final ConstantUtf8 u = (ConstantUtf8) c;
- final String key = u.getBytes();
- if (!utf8Table.containsKey(key)) {
- utf8Table.put(key, Integer.valueOf(i));
- }
- } else if (c instanceof ConstantCP) {
- final ConstantCP m = (ConstantCP) c;
- String className;
- ConstantUtf8 u8;
- if (c instanceof ConstantInvokeDynamic) {
- className = Integer.toString(((ConstantInvokeDynamic) m).getBootstrapMethodAttrIndex());
- } else if (c instanceof ConstantDynamic) {
- className = Integer.toString(((ConstantDynamic) m).getBootstrapMethodAttrIndex());
- } else {
- final ConstantClass clazz = (ConstantClass) constants[m.getClassIndex()];
- u8 = (ConstantUtf8) constants[clazz.getNameIndex()];
- className = Utility.pathToPackage(u8.getBytes());
- }
- final ConstantNameAndType n = (ConstantNameAndType) constants[m.getNameAndTypeIndex()];
- u8 = (ConstantUtf8) constants[n.getNameIndex()];
- final String methodName = u8.getBytes();
- u8 = (ConstantUtf8) constants[n.getSignatureIndex()];
- final String signature = u8.getBytes();
- // Since name cannot begin with digit, we can use METHODREF_DELIM without fear of duplicates
- String delim = METHODREF_DELIM;
- if (c instanceof ConstantInterfaceMethodref) {
- delim = IMETHODREF_DELIM;
- } else if (c instanceof ConstantFieldref) {
- delim = FIELDREF_DELIM;
- }
- sb.append(className);
- sb.append(delim);
- sb.append(methodName);
- sb.append(delim);
- sb.append(signature);
- final String key = sb.toString();
- sb.delete(0, sb.length());
- if (!cpTable.containsKey(key)) {
- cpTable.put(key, Integer.valueOf(i));
- }
- }
- // else if (c == null) { // entries may be null
- // // nothing to do
- // } else if (c instanceof ConstantInteger) {
- // // nothing to do
- // } else if (c instanceof ConstantLong) {
- // // nothing to do
- // } else if (c instanceof ConstantFloat) {
- // // nothing to do
- // } else if (c instanceof ConstantDouble) {
- // // nothing to do
- // } else if (c instanceof org.apache.bcel.classfile.ConstantMethodType) {
- // // TODO should this be handled somehow?
- // } else if (c instanceof org.apache.bcel.classfile.ConstantMethodHandle) {
- // // TODO should this be handled somehow?
- // } else if (c instanceof org.apache.bcel.classfile.ConstantModule) {
- // // TODO should this be handled somehow?
- // } else if (c instanceof org.apache.bcel.classfile.ConstantPackage) {
- // // TODO should this be handled somehow?
- // } else {
- // // Not helpful, should throw an exception.
- // assert false : "Unexpected constant type: " + c.getClass().getName();
- // }
- }
- }
- /**
- * Constructs a new instance with the given constant pool.
- *
- * @param cp the constant pool.
- */
- public ConstantPoolGen(final ConstantPool cp) {
- this(cp.getConstantPool());
- }
- /**
- * Add a reference to an array class (e.g. String[][]) as needed by MULTIANEWARRAY instruction, e.g. to the
- * ConstantPool.
- *
- * @param type type of array class
- * @return index of entry
- */
- public int addArrayClass(final ArrayType type) {
- return addClass_(type.getSignature());
- }
- /**
- * Add a new Class reference to the ConstantPool for a given type.
- *
- * @param type Class to add
- * @return index of entry
- */
- public int addClass(final ObjectType type) {
- return addClass(type.getClassName());
- }
- /**
- * Add a new Class reference to the ConstantPool, if it is not already in there.
- *
- * @param str Class to add
- * @return index of entry
- */
- public int addClass(final String str) {
- return addClass_(Utility.packageToPath(str));
- }
- private int addClass_(final String clazz) {
- final int cpRet;
- if ((cpRet = lookupClass(clazz)) != -1) {
- return cpRet; // Already in CP
- }
- adjustSize();
- final ConstantClass c = new ConstantClass(addUtf8(clazz));
- final int ret = index;
- constants[index++] = c;
- return computeIfAbsent(classTable, clazz, ret);
- }
- /**
- * Adds a constant from another ConstantPool and returns the new index.
- *
- * @param constant The constant to add.
- * @param cpGen Source pool.
- * @return index of entry
- */
- public int addConstant(final Constant constant, final ConstantPoolGen cpGen) {
- final Constant[] constants = cpGen.getConstantPool().getConstantPool();
- switch (constant.getTag()) {
- case Const.CONSTANT_String: {
- final ConstantString s = (ConstantString) constant;
- final ConstantUtf8 u8 = (ConstantUtf8) constants[s.getStringIndex()];
- return addString(u8.getBytes());
- }
- case Const.CONSTANT_Class: {
- final ConstantClass s = (ConstantClass) constant;
- final ConstantUtf8 u8 = (ConstantUtf8) constants[s.getNameIndex()];
- return addClass(u8.getBytes());
- }
- case Const.CONSTANT_NameAndType: {
- final ConstantNameAndType n = (ConstantNameAndType) constant;
- final ConstantUtf8 u8 = (ConstantUtf8) constants[n.getNameIndex()];
- final ConstantUtf8 u8_2 = (ConstantUtf8) constants[n.getSignatureIndex()];
- return addNameAndType(u8.getBytes(), u8_2.getBytes());
- }
- case Const.CONSTANT_Utf8:
- return addUtf8(((ConstantUtf8) constant).getBytes());
- case Const.CONSTANT_Double:
- return addDouble(((ConstantDouble) constant).getBytes());
- case Const.CONSTANT_Float:
- return addFloat(((ConstantFloat) constant).getBytes());
- case Const.CONSTANT_Long:
- return addLong(((ConstantLong) constant).getBytes());
- case Const.CONSTANT_Integer:
- return addInteger(((ConstantInteger) constant).getBytes());
- case Const.CONSTANT_InterfaceMethodref:
- case Const.CONSTANT_Methodref:
- case Const.CONSTANT_Fieldref: {
- final ConstantCP m = (ConstantCP) constant;
- final ConstantClass clazz = (ConstantClass) constants[m.getClassIndex()];
- final ConstantNameAndType n = (ConstantNameAndType) constants[m.getNameAndTypeIndex()];
- ConstantUtf8 u8 = (ConstantUtf8) constants[clazz.getNameIndex()];
- final String className = Utility.pathToPackage(u8.getBytes());
- u8 = (ConstantUtf8) constants[n.getNameIndex()];
- final String name = u8.getBytes();
- u8 = (ConstantUtf8) constants[n.getSignatureIndex()];
- final String signature = u8.getBytes();
- switch (constant.getTag()) {
- case Const.CONSTANT_InterfaceMethodref:
- return addInterfaceMethodref(className, name, signature);
- case Const.CONSTANT_Methodref:
- return addMethodref(className, name, signature);
- case Const.CONSTANT_Fieldref:
- return addFieldref(className, name, signature);
- default: // Never reached
- throw new IllegalArgumentException("Unknown constant type " + constant);
- }
- }
- default: // Never reached
- throw new IllegalArgumentException("Unknown constant type " + constant);
- }
- }
- /**
- * Add a new double constant to the ConstantPool, if it is not already in there.
- *
- * @param n Double number to add
- * @return index of entry
- */
- public int addDouble(final double n) {
- int ret;
- if ((ret = lookupDouble(n)) != -1) {
- return ret; // Already in CP
- }
- adjustSize();
- ret = index;
- constants[index] = new ConstantDouble(n);
- index += 2; // Wastes one entry according to spec
- return ret;
- }
- /**
- * Add a new Fieldref constant to the ConstantPool, if it is not already in there.
- *
- * @param className class name string to add
- * @param fieldName field name string to add
- * @param signature signature string to add
- * @return index of entry
- */
- public int addFieldref(final String className, final String fieldName, final String signature) {
- final int cpRet;
- if ((cpRet = lookupFieldref(className, fieldName, signature)) != -1) {
- return cpRet; // Already in CP
- }
- adjustSize();
- final int classIndex = addClass(className);
- final int nameAndTypeIndex = addNameAndType(fieldName, signature);
- final int ret = index;
- constants[index++] = new ConstantFieldref(classIndex, nameAndTypeIndex);
- return computeIfAbsent(cpTable, className + FIELDREF_DELIM + fieldName + FIELDREF_DELIM + signature, ret);
- }
- /**
- * Add a new Float constant to the ConstantPool, if it is not already in there.
- *
- * @param n Float number to add
- * @return index of entry
- */
- public int addFloat(final float n) {
- int ret;
- if ((ret = lookupFloat(n)) != -1) {
- return ret; // Already in CP
- }
- adjustSize();
- ret = index;
- constants[index++] = new ConstantFloat(n);
- return ret;
- }
- /**
- * Add a new Integer constant to the ConstantPool, if it is not already in there.
- *
- * @param n integer number to add
- * @return index of entry
- */
- public int addInteger(final int n) {
- int ret;
- if ((ret = lookupInteger(n)) != -1) {
- return ret; // Already in CP
- }
- adjustSize();
- ret = index;
- constants[index++] = new ConstantInteger(n);
- return ret;
- }
- public int addInterfaceMethodref(final MethodGen method) {
- return addInterfaceMethodref(method.getClassName(), method.getName(), method.getSignature());
- }
- /**
- * Add a new InterfaceMethodref constant to the ConstantPool, if it is not already in there.
- *
- * @param className class name string to add
- * @param methodName method name string to add
- * @param signature signature string to add
- * @return index of entry
- */
- public int addInterfaceMethodref(final String className, final String methodName, final String signature) {
- final int cpRet;
- if ((cpRet = lookupInterfaceMethodref(className, methodName, signature)) != -1) {
- return cpRet; // Already in CP
- }
- adjustSize();
- final int classIndex = addClass(className);
- final int nameAndTypeIndex = addNameAndType(methodName, signature);
- final int ret = index;
- constants[index++] = new ConstantInterfaceMethodref(classIndex, nameAndTypeIndex);
- return computeIfAbsent(cpTable, className + IMETHODREF_DELIM + methodName + IMETHODREF_DELIM + signature, ret);
- }
- /**
- * Add a new long constant to the ConstantPool, if it is not already in there.
- *
- * @param n Long number to add
- * @return index of entry
- */
- public int addLong(final long n) {
- int ret;
- if ((ret = lookupLong(n)) != -1) {
- return ret; // Already in CP
- }
- adjustSize();
- ret = index;
- constants[index] = new ConstantLong(n);
- index += 2; // Wastes one entry according to spec
- return ret;
- }
- public int addMethodref(final MethodGen method) {
- return addMethodref(method.getClassName(), method.getName(), method.getSignature());
- }
- /**
- * Add a new Methodref constant to the ConstantPool, if it is not already in there.
- *
- * @param className class name string to add
- * @param methodName method name string to add
- * @param signature method signature string to add
- * @return index of entry
- */
- public int addMethodref(final String className, final String methodName, final String signature) {
- final int cpRet;
- if ((cpRet = lookupMethodref(className, methodName, signature)) != -1) {
- return cpRet; // Already in CP
- }
- adjustSize();
- final int nameAndTypeIndex = addNameAndType(methodName, signature);
- final int classIndex = addClass(className);
- final int ret = index;
- constants[index++] = new ConstantMethodref(classIndex, nameAndTypeIndex);
- return computeIfAbsent(cpTable, className + METHODREF_DELIM + methodName + METHODREF_DELIM + signature, ret);
- }
- /**
- * Add a new NameAndType constant to the ConstantPool if it is not already in there.
- *
- * @param name Name string to add
- * @param signature signature string to add
- * @return index of entry
- */
- public int addNameAndType(final String name, final String signature) {
- int ret;
- if ((ret = lookupNameAndType(name, signature)) != -1) {
- return ret; // Already in CP
- }
- adjustSize();
- final int nameIndex = addUtf8(name);
- final int signatureIndex = addUtf8(signature);
- ret = index;
- constants[index++] = new ConstantNameAndType(nameIndex, signatureIndex);
- return computeIfAbsent(natTable, name + NAT_DELIM + signature, ret);
- }
- /**
- * Add a new String constant to the ConstantPool, if it is not already in there.
- *
- * @param str String to add
- * @return index of entry
- */
- public int addString(final String str) {
- int ret;
- if ((ret = lookupString(str)) != -1) {
- return ret; // Already in CP
- }
- final int utf8 = addUtf8(str);
- adjustSize();
- final ConstantString s = new ConstantString(utf8);
- ret = index;
- constants[index++] = s;
- return computeIfAbsent(stringTable, str, ret);
- }
- /**
- * Add a new Utf8 constant to the ConstantPool, if it is not already in there.
- *
- * @param n Utf8 string to add
- * @return index of entry
- */
- public int addUtf8(final String n) {
- int ret;
- if ((ret = lookupUtf8(n)) != -1) {
- return ret; // Already in CP
- }
- adjustSize();
- ret = index;
- constants[index++] = new ConstantUtf8(n);
- return computeIfAbsent(utf8Table, n, ret);
- }
- /**
- * Resize internal array of constants.
- */
- protected void adjustSize() {
- // 3 extra spaces are needed as some entries may take 3 slots
- if (index + 3 >= Const.MAX_CP_ENTRIES + 1) {
- throw new IllegalStateException("The number of constants " + (index + 3)
- + " is over the size of the constant pool: "
- + Const.MAX_CP_ENTRIES);
- }
- if (index + 3 >= size) {
- final Constant[] tmp = constants;
- size *= 2;
- // the constant array shall not exceed the size of the constant pool
- size = Math.min(size, Const.MAX_CP_ENTRIES + 1);
- constants = new Constant[size];
- System.arraycopy(tmp, 0, constants, 0, index);
- }
- }
- private int computeIfAbsent(final Map<String, Integer> map, final String key, final int value) {
- return map.computeIfAbsent(key, k -> Integer.valueOf(value));
- }
- /**
- * @param i index in constant pool
- * @return constant pool entry at index i
- */
- public Constant getConstant(final int i) {
- return constants[i];
- }
- /**
- * @return intermediate constant pool
- */
- public ConstantPool getConstantPool() {
- return new ConstantPool(constants);
- }
- /**
- * @return constant pool with proper length
- */
- public ConstantPool getFinalConstantPool() {
- return new ConstantPool(Arrays.copyOf(constants, index));
- }
- private int getIndex(final Map<String, Integer> map, final String key) {
- return toIndex(map.get(key));
- }
- /**
- * @return current size of constant pool
- */
- public int getSize() {
- return index;
- }
- /**
- * Look for ConstantClass in ConstantPool named 'str'.
- *
- * @param str String to search for
- * @return index on success, -1 otherwise
- */
- public int lookupClass(final String str) {
- return getIndex(classTable, Utility.packageToPath(str));
- }
- /**
- * Look for ConstantDouble in ConstantPool.
- *
- * @param n Double number to look for
- * @return index on success, -1 otherwise
- */
- public int lookupDouble(final double n) {
- final long bits = Double.doubleToLongBits(n);
- for (int i = 1; i < index; i++) {
- if (constants[i] instanceof ConstantDouble) {
- final ConstantDouble c = (ConstantDouble) constants[i];
- if (Double.doubleToLongBits(c.getBytes()) == bits) {
- return i;
- }
- }
- }
- return -1;
- }
- /**
- * Look for ConstantFieldref in ConstantPool.
- *
- * @param className Where to find method
- * @param fieldName Guess what
- * @param signature return and argument types
- * @return index on success, -1 otherwise
- */
- public int lookupFieldref(final String className, final String fieldName, final String signature) {
- return getIndex(cpTable, className + FIELDREF_DELIM + fieldName + FIELDREF_DELIM + signature);
- }
- /**
- * Look for ConstantFloat in ConstantPool.
- *
- * @param n Float number to look for
- * @return index on success, -1 otherwise
- */
- public int lookupFloat(final float n) {
- final int bits = Float.floatToIntBits(n);
- for (int i = 1; i < index; i++) {
- if (constants[i] instanceof ConstantFloat) {
- final ConstantFloat c = (ConstantFloat) constants[i];
- if (Float.floatToIntBits(c.getBytes()) == bits) {
- return i;
- }
- }
- }
- return -1;
- }
- /**
- * Look for ConstantInteger in ConstantPool.
- *
- * @param n integer number to look for
- * @return index on success, -1 otherwise
- */
- public int lookupInteger(final int n) {
- for (int i = 1; i < index; i++) {
- if (constants[i] instanceof ConstantInteger) {
- final ConstantInteger c = (ConstantInteger) constants[i];
- if (c.getBytes() == n) {
- return i;
- }
- }
- }
- return -1;
- }
- public int lookupInterfaceMethodref(final MethodGen method) {
- return lookupInterfaceMethodref(method.getClassName(), method.getName(), method.getSignature());
- }
- /**
- * Look for ConstantInterfaceMethodref in ConstantPool.
- *
- * @param className Where to find method
- * @param methodName Guess what
- * @param signature return and argument types
- * @return index on success, -1 otherwise
- */
- public int lookupInterfaceMethodref(final String className, final String methodName, final String signature) {
- return getIndex(cpTable, className + IMETHODREF_DELIM + methodName + IMETHODREF_DELIM + signature);
- }
- /**
- * Look for ConstantLong in ConstantPool.
- *
- * @param n Long number to look for
- * @return index on success, -1 otherwise
- */
- public int lookupLong(final long n) {
- for (int i = 1; i < index; i++) {
- if (constants[i] instanceof ConstantLong) {
- final ConstantLong c = (ConstantLong) constants[i];
- if (c.getBytes() == n) {
- return i;
- }
- }
- }
- return -1;
- }
- public int lookupMethodref(final MethodGen method) {
- return lookupMethodref(method.getClassName(), method.getName(), method.getSignature());
- }
- /**
- * Look for ConstantMethodref in ConstantPool.
- *
- * @param className Where to find method
- * @param methodName Guess what
- * @param signature return and argument types
- * @return index on success, -1 otherwise
- */
- public int lookupMethodref(final String className, final String methodName, final String signature) {
- return getIndex(cpTable, className + METHODREF_DELIM + methodName + METHODREF_DELIM + signature);
- }
- /**
- * Look for ConstantNameAndType in ConstantPool.
- *
- * @param name of variable/method
- * @param signature of variable/method
- * @return index on success, -1 otherwise
- */
- public int lookupNameAndType(final String name, final String signature) {
- return getIndex(natTable, name + NAT_DELIM + signature);
- }
- /**
- * Look for ConstantString in ConstantPool containing String 'str'.
- *
- * @param str String to search for
- * @return index on success, -1 otherwise
- */
- public int lookupString(final String str) {
- return getIndex(stringTable, str);
- }
- /**
- * Look for ConstantUtf8 in ConstantPool.
- *
- * @param n Utf8 string to look for
- * @return index on success, -1 otherwise
- */
- public int lookupUtf8(final String n) {
- return getIndex(utf8Table, n);
- }
- /**
- * Use with care!
- *
- * @param i index in constant pool
- * @param c new constant pool entry at index i
- */
- public void setConstant(final int i, final Constant c) {
- constants[i] = c;
- }
- private int toIndex(final Integer index) {
- return index != null ? index.intValue() : -1;
- }
- /**
- * @return String representation.
- */
- @Override
- public String toString() {
- final StringBuilder buf = new StringBuilder();
- for (int i = 1; i < index; i++) {
- buf.append(i).append(")").append(constants[i]).append("\n");
- }
- return buf.toString();
- }
- }