| /* |
| * 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.classfile; |
| |
| import java.io.DataInput; |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| |
| import org.apache.bcel.Const; |
| |
| /** |
| * 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 skips an entry after an 8-byte constant (double, |
| * long) entry. Those interested in generating constant pools |
| * programatically should see <a href="../generic/ConstantPoolGen.html"> |
| * ConstantPoolGen</a>. |
| |
| * @version $Id$ |
| * @see Constant |
| * @see org.apache.bcel.generic.ConstantPoolGen |
| */ |
| public class ConstantPool implements Cloneable, Node { |
| |
| private Constant[] constant_pool; |
| |
| |
| /** |
| * @param constant_pool Array of constants |
| */ |
| public ConstantPool(final Constant[] constant_pool) { |
| this.constant_pool = constant_pool; |
| } |
| |
| |
| /** |
| * Read constants from given input stream. |
| * |
| * @param input Input stream |
| * @throws IOException |
| * @throws ClassFormatException |
| */ |
| public ConstantPool(final DataInput input) throws IOException, ClassFormatException { |
| byte tag; |
| final int constant_pool_count = input.readUnsignedShort(); |
| constant_pool = new Constant[constant_pool_count]; |
| /* constant_pool[0] is unused by the compiler and may be used freely |
| * by the implementation. |
| */ |
| for (int i = 1; i < constant_pool_count; i++) { |
| constant_pool[i] = Constant.readConstant(input); |
| /* 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 pool, then the next item |
| * will be numbered n+2" |
| * |
| * Thus we have to increment the index counter. |
| */ |
| tag = constant_pool[i].getTag(); |
| if ((tag == Const.CONSTANT_Double) || (tag == Const.CONSTANT_Long)) { |
| i++; |
| } |
| } |
| } |
| |
| |
| /** |
| * Called by objects that are traversing the nodes of the tree implicitely |
| * defined by the contents of a Java class. I.e., the hierarchy of methods, |
| * fields, attributes, etc. spawns a tree of objects. |
| * |
| * @param v Visitor object |
| */ |
| @Override |
| public void accept( final Visitor v ) { |
| v.visitConstantPool(this); |
| } |
| |
| |
| /** |
| * Resolve constant to a string representation. |
| * |
| * @param c Constant to be printed |
| * @return String representation |
| */ |
| public String constantToString( Constant c ) throws ClassFormatException { |
| String str; |
| int i; |
| final byte tag = c.getTag(); |
| switch (tag) { |
| case Const.CONSTANT_Class: |
| i = ((ConstantClass) c).getNameIndex(); |
| c = getConstant(i, Const.CONSTANT_Utf8); |
| str = Utility.compactClassName(((ConstantUtf8) c).getBytes(), false); |
| break; |
| case Const.CONSTANT_String: |
| i = ((ConstantString) c).getStringIndex(); |
| c = getConstant(i, Const.CONSTANT_Utf8); |
| str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\""; |
| break; |
| case Const.CONSTANT_Utf8: |
| str = ((ConstantUtf8) c).getBytes(); |
| break; |
| case Const.CONSTANT_Double: |
| str = String.valueOf(((ConstantDouble) c).getBytes()); |
| break; |
| case Const.CONSTANT_Float: |
| str = String.valueOf(((ConstantFloat) c).getBytes()); |
| break; |
| case Const.CONSTANT_Long: |
| str = String.valueOf(((ConstantLong) c).getBytes()); |
| break; |
| case Const.CONSTANT_Integer: |
| str = String.valueOf(((ConstantInteger) c).getBytes()); |
| break; |
| case Const.CONSTANT_NameAndType: |
| str = constantToString(((ConstantNameAndType) c).getNameIndex(), |
| Const.CONSTANT_Utf8) |
| + ":" + constantToString(((ConstantNameAndType) c).getSignatureIndex(), |
| Const.CONSTANT_Utf8); |
| break; |
| case Const.CONSTANT_InterfaceMethodref: |
| case Const.CONSTANT_Methodref: |
| case Const.CONSTANT_Fieldref: |
| str = constantToString(((ConstantCP) c).getClassIndex(), Const.CONSTANT_Class) |
| + "." + constantToString(((ConstantCP) c).getNameAndTypeIndex(), |
| Const.CONSTANT_NameAndType); |
| break; |
| case Const.CONSTANT_MethodHandle: |
| // Note that the ReferenceIndex may point to a Fieldref, Methodref or |
| // InterfaceMethodref - so we need to peek ahead to get the actual type. |
| final ConstantMethodHandle cmh = (ConstantMethodHandle) c; |
| str = Const.getMethodHandleName(cmh.getReferenceKind()) |
| + " " + constantToString(cmh.getReferenceIndex(), |
| getConstant(cmh.getReferenceIndex()).getTag()); |
| break; |
| case Const.CONSTANT_MethodType: |
| final ConstantMethodType cmt = (ConstantMethodType) c; |
| str = constantToString(cmt.getDescriptorIndex(), Const.CONSTANT_Utf8); |
| break; |
| case Const.CONSTANT_InvokeDynamic: |
| final ConstantInvokeDynamic cid = (ConstantInvokeDynamic) c; |
| str = cid.getBootstrapMethodAttrIndex() |
| + ":" + constantToString(cid.getNameAndTypeIndex(), |
| Const.CONSTANT_NameAndType); |
| break; |
| default: // Never reached |
| throw new RuntimeException("Unknown constant type " + tag); |
| } |
| return str; |
| } |
| |
| |
| private static String escape( final String str ) { |
| final int len = str.length(); |
| final StringBuilder buf = new StringBuilder(len + 5); |
| final char[] ch = str.toCharArray(); |
| for (int i = 0; i < len; i++) { |
| switch (ch[i]) { |
| case '\n': |
| buf.append("\\n"); |
| break; |
| case '\r': |
| buf.append("\\r"); |
| break; |
| case '\t': |
| buf.append("\\t"); |
| break; |
| case '\b': |
| buf.append("\\b"); |
| break; |
| case '"': |
| buf.append("\\\""); |
| break; |
| default: |
| buf.append(ch[i]); |
| } |
| } |
| return buf.toString(); |
| } |
| |
| |
| /** |
| * Retrieve constant at `index' from constant pool and resolve it to |
| * a string representation. |
| * |
| * @param index of constant in constant pool |
| * @param tag expected type |
| * @return String representation |
| */ |
| public String constantToString( final int index, final byte tag ) throws ClassFormatException { |
| final Constant c = getConstant(index, tag); |
| return constantToString(c); |
| } |
| |
| |
| /** |
| * Dump constant pool to file stream in binary format. |
| * |
| * @param file Output file stream |
| * @throws IOException |
| */ |
| public void dump( final DataOutputStream file ) throws IOException { |
| file.writeShort(constant_pool.length); |
| for (int i = 1; i < constant_pool.length; i++) { |
| if (constant_pool[i] != null) { |
| constant_pool[i].dump(file); |
| } |
| } |
| } |
| |
| |
| /** |
| * Get constant from constant pool. |
| * |
| * @param index Index in constant pool |
| * @return Constant value |
| * @see Constant |
| */ |
| public Constant getConstant( final int index ) { |
| if (index >= constant_pool.length || index < 0) { |
| throw new ClassFormatException("Invalid constant pool reference: " + index |
| + ". Constant pool size is: " + constant_pool.length); |
| } |
| return constant_pool[index]; |
| } |
| |
| |
| /** |
| * Get constant from constant pool and check whether it has the |
| * expected type. |
| * |
| * @param index Index in constant pool |
| * @param tag Tag of expected constant, i.e., its type |
| * @return Constant value |
| * @see Constant |
| * @throws ClassFormatException |
| */ |
| public Constant getConstant( final int index, final byte tag ) throws ClassFormatException { |
| Constant c; |
| c = getConstant(index); |
| if (c == null) { |
| throw new ClassFormatException("Constant pool at index " + index + " is null."); |
| } |
| if (c.getTag() != tag) { |
| throw new ClassFormatException("Expected class `" + Const.getConstantName(tag) |
| + "' at index " + index + " and got " + c); |
| } |
| return c; |
| } |
| |
| |
| /** |
| * @return Array of constants. |
| * @see Constant |
| */ |
| public Constant[] getConstantPool() { |
| return constant_pool; |
| } |
| |
| |
| /** |
| * Get string from constant pool and bypass the indirection of |
| * `ConstantClass' and `ConstantString' objects. I.e. these classes have |
| * an index field that points to another entry of the constant pool of |
| * type `ConstantUtf8' which contains the real data. |
| * |
| * @param index Index in constant pool |
| * @param tag Tag of expected constant, either ConstantClass or ConstantString |
| * @return Contents of string reference |
| * @see ConstantClass |
| * @see ConstantString |
| * @throws ClassFormatException |
| */ |
| public String getConstantString( final int index, final byte tag ) throws ClassFormatException { |
| Constant c; |
| int i; |
| c = getConstant(index, tag); |
| /* This switch() is not that elegant, since the two classes have the |
| * same contents, they just differ in the name of the index |
| * field variable. |
| * But we 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 subclassing. |
| */ |
| switch (tag) { |
| case Const.CONSTANT_Class: |
| i = ((ConstantClass) c).getNameIndex(); |
| break; |
| case Const.CONSTANT_String: |
| i = ((ConstantString) c).getStringIndex(); |
| break; |
| default: |
| throw new RuntimeException("getConstantString called with illegal tag " + tag); |
| } |
| // Finally get the string from the constant pool |
| c = getConstant(i, Const.CONSTANT_Utf8); |
| return ((ConstantUtf8) c).getBytes(); |
| } |
| |
| |
| /** |
| * @return Length of constant pool. |
| */ |
| public int getLength() { |
| return constant_pool == null ? 0 : constant_pool.length; |
| } |
| |
| |
| /** |
| * @param constant Constant to set |
| */ |
| public void setConstant( final int index, final Constant constant ) { |
| constant_pool[index] = constant; |
| } |
| |
| |
| /** |
| * @param constant_pool |
| */ |
| public void setConstantPool( final Constant[] constant_pool ) { |
| this.constant_pool = constant_pool; |
| } |
| |
| |
| /** |
| * @return String representation. |
| */ |
| @Override |
| public String toString() { |
| final StringBuilder buf = new StringBuilder(); |
| for (int i = 1; i < constant_pool.length; i++) { |
| buf.append(i).append(")").append(constant_pool[i]).append("\n"); |
| } |
| return buf.toString(); |
| } |
| |
| |
| /** |
| * @return deep copy of this constant pool |
| */ |
| public ConstantPool copy() { |
| ConstantPool c = null; |
| try { |
| c = (ConstantPool) clone(); |
| c.constant_pool = new Constant[constant_pool.length]; |
| for (int i = 1; i < constant_pool.length; i++) { |
| if (constant_pool[i] != null) { |
| c.constant_pool[i] = constant_pool[i].copy(); |
| } |
| } |
| } catch (final CloneNotSupportedException e) { |
| // TODO should this throw? |
| } |
| return c; |
| } |
| } |