ConstantPool.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.classfile;

  18. import java.io.DataInput;
  19. import java.io.DataOutputStream;
  20. import java.io.IOException;
  21. import java.util.Arrays;
  22. import java.util.Iterator;

  23. import org.apache.bcel.Const;

  24. /**
  25.  * This class represents the constant pool, i.e., a table of constants, of a parsed classfile. It may contain null references, due to the JVM specification that
  26.  * skips an entry after an 8-byte constant (double, long) entry. Those interested in generating constant pools programmatically should see
  27.  * <a href="../generic/ConstantPoolGen.html"> ConstantPoolGen</a>.
  28.  *
  29.  * @see Constant
  30.  * @see org.apache.bcel.generic.ConstantPoolGen
  31.  */
  32. public class ConstantPool implements Cloneable, Node, Iterable<Constant> {

  33.     private static String escape(final String str) {
  34.         final int len = str.length();
  35.         final StringBuilder buf = new StringBuilder(len + 5);
  36.         final char[] ch = str.toCharArray();
  37.         for (int i = 0; i < len; i++) {
  38.             switch (ch[i]) {
  39.             case '\n':
  40.                 buf.append("\\n");
  41.                 break;
  42.             case '\r':
  43.                 buf.append("\\r");
  44.                 break;
  45.             case '\t':
  46.                 buf.append("\\t");
  47.                 break;
  48.             case '\b':
  49.                 buf.append("\\b");
  50.                 break;
  51.             case '"':
  52.                 buf.append("\\\"");
  53.                 break;
  54.             default:
  55.                 buf.append(ch[i]);
  56.             }
  57.         }
  58.         return buf.toString();
  59.     }

  60.     private Constant[] constantPool;

  61.     /**
  62.      * @param constantPool Array of constants
  63.      */
  64.     public ConstantPool(final Constant[] constantPool) {
  65.         setConstantPool(constantPool);
  66.     }

  67.     /**
  68.      * Reads constants from given input stream.
  69.      *
  70.      * @param input Input stream
  71.      * @throws IOException if problem in readUnsignedShort or readConstant
  72.      */
  73.     public ConstantPool(final DataInput input) throws IOException {
  74.         byte tag;
  75.         final int constantPoolCount = input.readUnsignedShort();
  76.         constantPool = new Constant[constantPoolCount];
  77.         /*
  78.          * constantPool[0] is unused by the compiler and may be used freely by the implementation.
  79.          * constantPool[0] is currently unused by the implementation.
  80.          */
  81.         for (int i = 1; i < constantPoolCount; i++) {
  82.             constantPool[i] = Constant.readConstant(input);
  83.             /*
  84.              * Quote from the JVM specification: "All eight byte constants take up two spots in the constant pool. If this is the n'th byte in the constant
  85.              * pool, then the next item will be numbered n+2"
  86.              *
  87.              * Thus we have to increment the index counter.
  88.              */
  89.             tag = constantPool[i].getTag();
  90.             if (tag == Const.CONSTANT_Double || tag == Const.CONSTANT_Long) {
  91.                 i++;
  92.             }
  93.         }
  94.     }

  95.     /**
  96.      * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. I.e., the hierarchy of methods, fields,
  97.      * attributes, etc. spawns a tree of objects.
  98.      *
  99.      * @param v Visitor object
  100.      */
  101.     @Override
  102.     public void accept(final Visitor v) {
  103.         v.visitConstantPool(this);
  104.     }

  105.     /**
  106.      * Resolves constant to a string representation.
  107.      *
  108.      * @param c Constant to be printed
  109.      * @return String representation
  110.      * @throws IllegalArgumentException if c is unknown constant type
  111.      */
  112.     public String constantToString(Constant c) throws IllegalArgumentException {
  113.         String str;
  114.         int i;
  115.         final byte tag = c.getTag();
  116.         switch (tag) {
  117.         case Const.CONSTANT_Class:
  118.             i = ((ConstantClass) c).getNameIndex();
  119.             c = getConstantUtf8(i);
  120.             str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
  121.             break;
  122.         case Const.CONSTANT_String:
  123.             i = ((ConstantString) c).getStringIndex();
  124.             c = getConstantUtf8(i);
  125.             str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\"";
  126.             break;
  127.         case Const.CONSTANT_Utf8:
  128.             str = ((ConstantUtf8) c).getBytes();
  129.             break;
  130.         case Const.CONSTANT_Double:
  131.             str = String.valueOf(((ConstantDouble) c).getBytes());
  132.             break;
  133.         case Const.CONSTANT_Float:
  134.             str = String.valueOf(((ConstantFloat) c).getBytes());
  135.             break;
  136.         case Const.CONSTANT_Long:
  137.             str = String.valueOf(((ConstantLong) c).getBytes());
  138.             break;
  139.         case Const.CONSTANT_Integer:
  140.             str = String.valueOf(((ConstantInteger) c).getBytes());
  141.             break;
  142.         case Const.CONSTANT_NameAndType:
  143.             str = constantToString(((ConstantNameAndType) c).getNameIndex(), Const.CONSTANT_Utf8) + " "
  144.                     + constantToString(((ConstantNameAndType) c).getSignatureIndex(), Const.CONSTANT_Utf8);
  145.             break;
  146.         case Const.CONSTANT_InterfaceMethodref:
  147.         case Const.CONSTANT_Methodref:
  148.         case Const.CONSTANT_Fieldref:
  149.             str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class) + "."
  150.                     + constantToString(((ConstantCP) c).getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
  151.             break;
  152.         case Const.CONSTANT_MethodHandle:
  153.             // Note that the ReferenceIndex may point to a Fieldref, Methodref or
  154.             // InterfaceMethodref - so we need to peek ahead to get the actual type.
  155.             final ConstantMethodHandle cmh = (ConstantMethodHandle) c;
  156.             str = Const.getMethodHandleName(cmh.getReferenceKind()) + " "
  157.                     + constantToString(cmh.getReferenceIndex(), getConstant(cmh.getReferenceIndex()).getTag());
  158.             break;
  159.         case Const.CONSTANT_MethodType:
  160.             final ConstantMethodType cmt = (ConstantMethodType) c;
  161.             str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8);
  162.             break;
  163.         case Const.CONSTANT_InvokeDynamic:
  164.             final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c;
  165.             str = cid.getBootstrapMethodAttrIndex() + ":" + constantToString(cid.getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
  166.             break;
  167.         case Const.CONSTANT_Dynamic:
  168.             final ConstantDynamic cd = (ConstantDynamic) c;
  169.             str = cd.getBootstrapMethodAttrIndex() + ":" + constantToString(cd.getNameAndTypeIndex(), Const.CONSTANT_NameAndType);
  170.             break;
  171.         case Const.CONSTANT_Module:
  172.             i = ((ConstantModule) c).getNameIndex();
  173.             c = getConstantUtf8(i);
  174.             str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
  175.             break;
  176.         case Const.CONSTANT_Package:
  177.             i = ((ConstantPackage) c).getNameIndex();
  178.             c = getConstantUtf8(i);
  179.             str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false);
  180.             break;
  181.         default: // Never reached
  182.             throw new IllegalArgumentException("Unknown constant type " + tag);
  183.         }
  184.         return str;
  185.     }

  186.     /**
  187.      * Retrieves constant at 'index' from constant pool and resolve it to a string representation.
  188.      *
  189.      * @param index of constant in constant pool
  190.      * @param tag   expected type
  191.      * @return String representation
  192.      */
  193.     public String constantToString(final int index, final byte tag) {
  194.         return constantToString(getConstant(index, tag));
  195.     }

  196.     /**
  197.      * @return deep copy of this constant pool
  198.      */
  199.     public ConstantPool copy() {
  200.         ConstantPool c = null;
  201.         try {
  202.             c = (ConstantPool) clone();
  203.             c.constantPool = new Constant[constantPool.length];
  204.             for (int i = 1; i < constantPool.length; i++) {
  205.                 if (constantPool[i] != null) {
  206.                     c.constantPool[i] = constantPool[i].copy();
  207.                 }
  208.             }
  209.         } catch (final CloneNotSupportedException e) {
  210.             // TODO should this throw?
  211.         }
  212.         return c;
  213.     }

  214.     /**
  215.      * Dump constant pool to file stream in binary format.
  216.      *
  217.      * @param file Output file stream
  218.      * @throws IOException if problem in writeShort or dump
  219.      */
  220.     public void dump(final DataOutputStream file) throws IOException {
  221.         /*
  222.          * Constants over the size of the constant pool shall not be written out. This is a redundant measure as the ConstantPoolGen should have already
  223.          * reported an error back in the situation.
  224.          */
  225.         final int size = Math.min(constantPool.length, Const.MAX_CP_ENTRIES);

  226.         file.writeShort(size);
  227.         for (int i = 1; i < size; i++) {
  228.             if (constantPool[i] != null) {
  229.                 constantPool[i].dump(file);
  230.             }
  231.         }
  232.     }

  233.     /**
  234.      * Gets constant from constant pool.
  235.      *
  236.      * @param index Index in constant pool
  237.      * @return Constant value
  238.      * @see Constant
  239.      * @throws ClassFormatException if index is invalid
  240.      */
  241.     @SuppressWarnings("unchecked")
  242.     public <T extends Constant> T getConstant(final int index) throws ClassFormatException {
  243.         return (T) getConstant(index, Constant.class);
  244.     }

  245.     /**
  246.      * Gets constant from constant pool and check whether it has the expected type.
  247.      *
  248.      * @param index Index in constant pool
  249.      * @param tag   Tag of expected constant, i.e., its type
  250.      * @return Constant value
  251.      * @see Constant
  252.      * @throws ClassFormatException if constant type does not match tag
  253.      */
  254.     @SuppressWarnings("unchecked")
  255.     public <T extends Constant> T getConstant(final int index, final byte tag) throws ClassFormatException {
  256.         return (T) getConstant(index, tag, Constant.class);
  257.     }

  258.     /**
  259.      * Gets constant from constant pool and check whether it has the expected type.
  260.      *
  261.      * @param index Index in constant pool
  262.      * @param tag   Tag of expected constant, i.e., its type
  263.      * @return Constant value
  264.      * @see Constant
  265.      * @throws ClassFormatException if constant type does not match tag
  266.      * @since 6.6.0
  267.      */
  268.     public <T extends Constant> T getConstant(final int index, final byte tag, final Class<T> castTo) throws ClassFormatException {
  269.         final T c = getConstant(index);
  270.         if (c == null || c.getTag() != tag) {
  271.             throw new ClassFormatException("Expected class '" + Const.getConstantName(tag) + "' at index " + index + " and got " + c);
  272.         }
  273.         return c;
  274.     }

  275.     /**
  276.      * Gets constant from constant pool.
  277.      *
  278.      * @param <T> A {@link Constant} subclass
  279.      * @param index Index in constant pool
  280.      * @param castTo The {@link Constant} subclass to cast to.
  281.      * @return Constant value
  282.      * @see Constant
  283.      * @throws ClassFormatException if index is invalid
  284.      * @since 6.6.0
  285.      */
  286.     public <T extends Constant> T getConstant(final int index, final Class<T> castTo) throws ClassFormatException {
  287.         if (index >= constantPool.length || index < 1) {
  288.             throw new ClassFormatException("Invalid constant pool reference using index: " + index + ". Constant pool size is: " + constantPool.length);
  289.         }
  290.         if (constantPool[index] != null && !castTo.isAssignableFrom(constantPool[index].getClass())) {
  291.             throw new ClassFormatException("Invalid constant pool reference at index: " + index +
  292.                     ". Expected " + castTo + " but was " + constantPool[index].getClass());
  293.         }
  294.         if (index > 1) {
  295.             final Constant prev = constantPool[index - 1];
  296.             if (prev != null && (prev.getTag() == Const.CONSTANT_Double || prev.getTag() == Const.CONSTANT_Long)) {
  297.                 throw new ClassFormatException("Constant pool at index " + index + " is invalid. The index is unused due to the preceeding "
  298.                         + Const.getConstantName(prev.getTag()) + ".");
  299.             }
  300.         }
  301.         // Previous check ensures this won't throw a ClassCastException
  302.         final T c = castTo.cast(constantPool[index]);
  303.         if (c == null) {
  304.             throw new ClassFormatException("Constant pool at index " + index + " is null.");
  305.         }
  306.         return c;
  307.     }

  308.     /**
  309.      * Gets constant from constant pool and check whether it has the expected type.
  310.      *
  311.      * @param index Index in constant pool
  312.      * @return ConstantInteger value
  313.      * @see ConstantInteger
  314.      * @throws ClassFormatException if constant type does not match tag
  315.      */
  316.     public ConstantInteger getConstantInteger(final int index) {
  317.         return getConstant(index, Const.CONSTANT_Integer, ConstantInteger.class);
  318.     }

  319.     /**
  320.      * @return Array of constants.
  321.      * @see Constant
  322.      */
  323.     public Constant[] getConstantPool() {
  324.         return constantPool;
  325.     }

  326.     /**
  327.      * Gets string from constant pool and bypass the indirection of 'ConstantClass' and 'ConstantString' objects. I.e. these classes have an index field that
  328.      * points to another entry of the constant pool of type 'ConstantUtf8' which contains the real data.
  329.      *
  330.      * @param index Index in constant pool
  331.      * @param tag   Tag of expected constant, either ConstantClass or ConstantString
  332.      * @return Contents of string reference
  333.      * @see ConstantClass
  334.      * @see ConstantString
  335.      * @throws IllegalArgumentException if tag is invalid
  336.      */
  337.     public String getConstantString(final int index, final byte tag) throws IllegalArgumentException {
  338.         int i;
  339.         /*
  340.          * This switch() is not that elegant, since the four classes have the same contents, they just differ in the name of the index field variable. But we
  341.          * want to stick to the JVM naming conventions closely though we could have solved these more elegantly by using the same variable name or by
  342.          * subclassing.
  343.          */
  344.         switch (tag) {
  345.         case Const.CONSTANT_Class:
  346.             i = getConstant(index, ConstantClass.class).getNameIndex();
  347.             break;
  348.         case Const.CONSTANT_String:
  349.             i = getConstant(index, ConstantString.class).getStringIndex();
  350.             break;
  351.         case Const.CONSTANT_Module:
  352.             i = getConstant(index, ConstantModule.class).getNameIndex();
  353.             break;
  354.         case Const.CONSTANT_Package:
  355.             i = getConstant(index, ConstantPackage.class).getNameIndex();
  356.             break;
  357.         case Const.CONSTANT_Utf8:
  358.             return getConstantUtf8(index).getBytes();
  359.         default:
  360.             throw new IllegalArgumentException("getConstantString called with illegal tag " + tag);
  361.         }
  362.         // Finally get the string from the constant pool
  363.         return getConstantUtf8(i).getBytes();
  364.     }

  365.     /**
  366.      * Gets constant from constant pool and check whether it has the expected type.
  367.      *
  368.      * @param index Index in constant pool
  369.      * @return ConstantUtf8 value
  370.      * @see ConstantUtf8
  371.      * @throws ClassFormatException if constant type does not match tag
  372.      */
  373.     public ConstantUtf8 getConstantUtf8(final int index) throws ClassFormatException {
  374.         return getConstant(index, Const.CONSTANT_Utf8, ConstantUtf8.class);
  375.     }

  376.     /**
  377.      * @return Length of constant pool.
  378.      */
  379.     public int getLength() {
  380.         return constantPool.length;
  381.     }

  382.     @Override
  383.     public Iterator<Constant> iterator() {
  384.         return Arrays.stream(constantPool).iterator();
  385.     }

  386.     /**
  387.      * @param constant Constant to set
  388.      */
  389.     public void setConstant(final int index, final Constant constant) {
  390.         constantPool[index] = constant;
  391.     }

  392.     /**
  393.      * @param constantPool
  394.      */
  395.     public void setConstantPool(final Constant[] constantPool) {
  396.         this.constantPool = constantPool != null ? constantPool : Constant.EMPTY_ARRAY;
  397.     }

  398.     /**
  399.      * @return String representation.
  400.      */
  401.     @Override
  402.     public String toString() {
  403.         final StringBuilder buf = new StringBuilder();
  404.         for (int i = 1; i < constantPool.length; i++) {
  405.             buf.append(i).append(")").append(constantPool[i]).append("\n");
  406.         }
  407.         return buf.toString();
  408.     }
  409. }