blob: c290a623fbbd0daf30dfef6eae4ff3557277abeb [file] [log] [blame]
/*
* 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.
*/
/*
* Contributor(s): Thomas Ball
*/
package org.netbeans.modules.classfile;
import java.io.*;
import java.util.*;
/**
* Class representing a Java class file constant pool.
*
* @author Thomas Ball
*/
public final class ConstantPool {
private static final int CONSTANT_POOL_START = 1;
// Constant Type enums (JVM spec table 4.3)
static final int CONSTANT_Utf8 = 1;
static final int CONSTANT_Integer = 3;
static final int CONSTANT_Float = 4;
static final int CONSTANT_Long = 5;
static final int CONSTANT_Double = 6;
static final int CONSTANT_Class = 7;
static final int CONSTANT_String = 8;
static final int CONSTANT_FieldRef = 9;
static final int CONSTANT_MethodRef = 10;
static final int CONSTANT_InterfaceMethodRef = 11;
static final int CONSTANT_NameAndType = 12;
static final int CONSTANT_MethodHandle = 15;
static final int CONSTANT_MethodType = 16;
static final int CONSTANT_ConstantDynamic = 17;
static final int CONSTANT_InvokeDynamic = 18;
static final int CONSTANT_Module = 19;
static final int CONSTANT_Package = 20;
CPEntry[] cpEntries;
int constantPoolCount = 0;
/**
* Create a ConstantPool object from a stream of bytes.
*
* @param size number of entries in this constant pool.
* @param bytes a stream of bytes defining the constant pool.
*/
/* package-private */ ConstantPool(int size, InputStream bytes)
throws IOException {
if (size < 0)
throw new IllegalArgumentException("size cannot be negative");
if (bytes == null)
throw new IllegalArgumentException("byte stream not specified");
constantPoolCount = size;
cpEntries = new CPEntry[constantPoolCount];
load(bytes);
}
/**
* Create a new ConstantPool object with no entries.
* NOTE: not supported until classfile writing is.
*/
/*public*/ ConstantPool() {
constantPoolCount = CONSTANT_POOL_START;
cpEntries = new CPEntry[constantPoolCount];
}
/**
* Get the CPEntry at a specific constant pool index.
*
* @param index the constant pool index for the entry
*/
public final CPEntry get(int index) {
if (index <= 0 || index >= cpEntries.length)
throw new IndexOutOfBoundsException(Integer.toString(index));
return cpEntries[index];
}
/**
* Get the CPClassInfo at a specific index.
*
* @param index the constant pool index for the entry
*/
public final CPClassInfo getClass(int index) {
if (index <= 0)
throw new IndexOutOfBoundsException(Integer.toString(index));
return (CPClassInfo)get(index);
}
/* Return an array of all constants of a specified class type.
*
* @param type the constant pool type to return.
*/
public final <T extends CPEntry> Collection<? extends T> getAllConstants(Class<T> classType) {
return Collections.unmodifiableCollection(
getAllConstantsImpl(classType));
}
private <T extends CPEntry> Collection<? extends T> getAllConstantsImpl(Class<T> classType) {
int n = cpEntries.length;
Collection<T> c = new ArrayList<T>(n);
for (int i = CONSTANT_POOL_START; i < n; i++) {
if (cpEntries[i] != null &&
cpEntries[i].getClass().equals(classType)) {
c.add(classType.cast(cpEntries[i]));
}
}
return c;
}
/* Return the collection of all unique class references in pool.
*
* @return a Set of ClassNames specifying the referenced classnames.
*
* @deprecated use <code>ClassFile.getAllClassNames()</code>,
* as all class references cannot be reliably determined from just
* the constant pool structure.
*/
public final Set<ClassName> getAllClassNames() {
Set<ClassName> set = new HashSet<ClassName>();
// include all class name constants
Collection<? extends CPEntry> c = getAllConstantsImpl(CPClassInfo.class);
for (Iterator<? extends CPEntry> i = c.iterator(); i.hasNext();) {
CPClassInfo ci = (CPClassInfo)i.next();
set.add(ci.getClassName());
}
return Collections.unmodifiableSet(set);
}
final String getString(int index) {
CPUTF8Info utf = (CPUTF8Info)cpEntries[index];
return utf.getName();
}
private void load(InputStream cpBytes) throws IOException {
try {
ConstantPoolReader cpr = new ConstantPoolReader(cpBytes);
// Read in pool entries.
for (int i = CONSTANT_POOL_START; i < constantPoolCount; i++) {
CPEntry newEntry = getConstantPoolEntry(cpr);
cpEntries[i] = newEntry;
if (newEntry.usesTwoSlots())
i++;
}
// Resolve pool entries.
for (int i = CONSTANT_POOL_START; i < constantPoolCount; i++) {
CPEntry entry = cpEntries[i];
if (entry == null) {
continue;
}
entry.resolve(cpEntries);
}
} catch (IllegalArgumentException ioe) {
throw new InvalidClassFormatException(ioe);
} catch (IndexOutOfBoundsException iobe) {
throw new InvalidClassFormatException(iobe);
}
}
private CPEntry getConstantPoolEntry(ConstantPoolReader cpr)
throws IOException {
CPEntry newEntry;
byte type = cpr.readByte();
switch (type) {
case CONSTANT_Utf8:
newEntry = new CPUTF8Info(this, cpr.readRawUTF());
break;
case CONSTANT_Integer:
newEntry = new CPIntegerInfo(this, cpr.readInt());
break;
case CONSTANT_Float:
newEntry = new CPFloatInfo(this, cpr.readFloat());
break;
case CONSTANT_Long:
newEntry = new CPLongInfo(this, cpr.readLong());
break;
case CONSTANT_Double:
newEntry = new CPDoubleInfo(this, cpr.readDouble());
break;
case CONSTANT_Class: {
int nameIndex = cpr.readUnsignedShort();
newEntry = new CPClassInfo(this, nameIndex);
break;
}
case CONSTANT_String: {
int nameIndex = cpr.readUnsignedShort();
newEntry = new CPStringInfo(this, nameIndex);
break;
}
case CONSTANT_FieldRef: {
int classIndex = cpr.readUnsignedShort();
int natIndex = cpr.readUnsignedShort();
newEntry = new CPFieldInfo(this, classIndex, natIndex);
break;
}
case CONSTANT_MethodRef: {
int classIndex = cpr.readUnsignedShort();
int natIndex = cpr.readUnsignedShort();
newEntry = new CPMethodInfo(this, classIndex, natIndex);
break;
}
case CONSTANT_InterfaceMethodRef: {
int classIndex = cpr.readUnsignedShort();
int natIndex = cpr.readUnsignedShort();
newEntry = new CPInterfaceMethodInfo(this, classIndex, natIndex);
break;
}
case CONSTANT_NameAndType: {
int nameIndex = cpr.readUnsignedShort();
int descIndex = cpr.readUnsignedShort();
newEntry = new CPNameAndTypeInfo(this, nameIndex, descIndex);
break;
}
case CONSTANT_MethodHandle: {
int kind = cpr.readUnsignedByte();
int index = cpr.readUnsignedShort();
newEntry = new CPMethodHandleInfo(this, kind, index);
break;
}
case CONSTANT_MethodType: {
int index = cpr.readUnsignedShort();
newEntry = new CPMethodTypeInfo(this, index);
break;
}
case CONSTANT_ConstantDynamic: {
int bootstrapMethod = cpr.readUnsignedShort();
int nameAndType = cpr.readUnsignedShort();
newEntry = new CPConstantDynamicInfo(this, bootstrapMethod, nameAndType);
break;
}
case CONSTANT_InvokeDynamic: {
int bootstrapMethod = cpr.readUnsignedShort();
int nameAndType = cpr.readUnsignedShort();
newEntry = new CPInvokeDynamicInfo(this, bootstrapMethod, nameAndType);
break;
}
case CONSTANT_Module: {
int nameIndex = cpr.readUnsignedShort();
newEntry = new CPModuleInfo(this, nameIndex);
break;
}
case CONSTANT_Package: {
int nameIndex = cpr.readUnsignedShort();
newEntry = new CPPackageInfo(this, nameIndex);
break;
}
default:
throw new IllegalArgumentException(
"invalid constant pool type: " + type);
}
return newEntry;
}
}