
com.ibm.wala.shrike.shrikeCT.ConstantPoolParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.ibm.wala.shrike Show documentation
Show all versions of com.ibm.wala.shrike Show documentation
T. J. Watson Libraries for Analysis
The newest version!
/*
* Copyright (c) 2002,2006 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*/
package com.ibm.wala.shrike.shrikeCT;
import com.ibm.wala.shrike.shrikeCT.BootstrapMethodsReader.BootstrapMethod;
import com.ibm.wala.shrike.shrikeCT.ClassReader.AttrIterator;
/** A ConstantPoolParser provides read-only access to the constant pool of a class file. */
public final class ConstantPoolParser implements ClassConstants {
public static class ReferenceToken {
private final byte kind;
private final String className;
private final String elementName;
private final String descriptor;
public ReferenceToken(byte kind, String className, String elementName, String descriptor) {
this.kind = kind;
this.className = className;
this.elementName = elementName;
this.descriptor = descriptor;
}
public byte getKind() {
return kind;
}
public String getClassName() {
return className;
}
public String getElementName() {
return elementName;
}
public String getDescriptor() {
return descriptor;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((className == null) ? 0 : className.hashCode());
result = prime * result + ((descriptor == null) ? 0 : descriptor.hashCode());
result = prime * result + ((elementName == null) ? 0 : elementName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
ReferenceToken other = (ReferenceToken) obj;
if (kind != other.kind) {
return false;
}
if (className == null) {
if (other.className != null) return false;
} else if (!className.equals(other.className)) return false;
if (descriptor == null) {
if (other.descriptor != null) return false;
} else if (!descriptor.equals(other.descriptor)) return false;
if (elementName == null) {
if (other.elementName != null) return false;
} else if (!elementName.equals(other.elementName)) return false;
return true;
}
}
private final byte[] bytes;
private int[] cpOffsets;
private String[] cpItems;
private BootstrapMethodsReader invokeDynamicBootstraps;
// TODO: use JVM spec limit here?
private static final int MAX_CP_ITEMS = Integer.MAX_VALUE / 4;
private BootstrapMethodsReader getBootstrapReader() throws InvalidClassFileException {
if (invokeDynamicBootstraps == null) {
ClassReader thisClass = new ClassReader(bytes);
AttrIterator attrs = new AttrIterator();
thisClass.initClassAttributeIterator(attrs);
for (; attrs.isValid(); attrs.advance()) {
if (attrs.getName().equals("BootstrapMethods")) {
invokeDynamicBootstraps = new BootstrapMethodsReader(attrs);
break;
}
}
assert invokeDynamicBootstraps != null;
}
return invokeDynamicBootstraps;
}
/**
* @param bytes the raw class file data
* @param offset the start of the constant pool data
* @param itemCount the number of items in the pool
*/
public ConstantPoolParser(byte[] bytes, int offset, int itemCount)
throws InvalidClassFileException {
this.bytes = bytes;
if (offset < 0) {
throw new IllegalArgumentException("invalid offset: " + offset);
}
if (itemCount < 0 || itemCount > MAX_CP_ITEMS) {
throw new IllegalArgumentException("invalid itemCount: " + itemCount);
}
parseConstantPool(offset, itemCount);
}
/**
* @return the buffer holding the raw class file data
*/
public byte[] getRawBytes() {
return bytes;
}
/**
* @return the offset of the constant pool data in the raw class file buffer
*/
public int getRawOffset() throws IllegalStateException {
if (cpOffsets.length < 2) {
throw new IllegalStateException();
}
return cpOffsets[1];
}
/**
* @return the size of the constant pool data in the raw class file buffer
*/
public int getRawSize() throws IllegalStateException {
if (cpOffsets.length < 2) {
throw new IllegalStateException();
}
return cpOffsets[cpOffsets.length - 1] - cpOffsets[1];
}
/**
* @return the number of constant pool items (maximum item index plus one)
*/
public int getItemCount() {
return cpOffsets.length - 1;
}
private void checkLength(int offset, int required) throws InvalidClassFileException {
if (bytes.length < offset + required) {
throw new InvalidClassFileException(
offset,
"file truncated, expected " + required + " bytes, saw only " + (bytes.length - offset));
}
}
/**
* @return the type of constant pool item i, or 0 if i is an unused constant pool item
*/
public byte getItemType(int i) throws IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0) {
return 0;
} else {
return getByte(offset);
}
}
/**
* @return the name of the Class at constant pool item i, in JVM format (e.g., java/lang/Object)
*/
public String getCPClass(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_Class) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a Class");
}
String s = cpItems[i];
if (s == null) {
try {
s = getCPUtf8(getUShort(offset + 1));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid class name at constant pool item #" + i + ": " + ex.getMessage());
}
cpItems[i] = s;
}
return s;
}
/**
* @return the name of the method at constant pool item i, in JVM format (e.g., java/lang/Object)
*/
public String getCPMethodType(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_MethodType) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a MethodType");
}
String s = cpItems[i];
if (s == null) {
try {
s = getCPUtf8(getUShort(offset + 1));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid method type at constant pool item #" + i + ": " + ex.getMessage());
}
cpItems[i] = s;
}
return s;
}
/**
* @return the String at constant pool item i
*/
public String getCPString(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_String) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a String");
}
String s = cpItems[i];
if (s == null) {
try {
s = getCPUtf8(getUShort(offset + 1));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid string at constant pool item #" + i + ": " + ex.getMessage());
}
cpItems[i] = s;
}
return s;
}
/** Does b represent the tag of a constant pool reference to an (interface) method or field? */
public static boolean isRef(byte b) {
switch (b) {
case CONSTANT_MethodRef:
case CONSTANT_FieldRef:
case CONSTANT_InterfaceMethodRef:
return true;
default:
return false;
}
}
/**
* @return the name of the class part of the FieldRef, MethodRef, or InterfaceMethodRef at
* constant pool item i
*/
public String getCPRefClass(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || !isRef(getByte(offset))) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a Ref");
}
try {
return getCPClass(getUShort(offset + 1));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid Ref class at constant pool item #" + i + ": " + ex.getMessage());
}
}
/**
* @return the name part of the FieldRef, MethodRef, or InterfaceMethodRef at constant pool item i
*/
public String getCPRefName(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || !isRef(getByte(offset))) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a Ref");
}
try {
return getCPNATName(getUShort(offset + 3));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid Ref NameAndType at constant pool item #" + i + ": " + ex.getMessage());
}
}
/**
* @return the type part of the FieldRef, MethodRef, or InterfaceMethodRef at constant pool item
* i, in JVM format (e.g., I, Z, or Ljava/lang/Object;)
*/
public String getCPRefType(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || !isRef(getByte(offset))) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a Ref");
}
try {
return getCPNATType(getUShort(offset + 3));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid Ref NameAndType at constant pool item #" + i + ": " + ex.getMessage());
}
}
/**
* @return the name part of the NameAndType at constant pool item i
*/
public String getCPNATName(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_NameAndType) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a NameAndType");
}
try {
return getCPUtf8(getUShort(offset + 1));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid NameAndType name at constant pool item #" + i + ": " + ex.getMessage());
}
}
/**
* @return the type part of the NameAndType at constant pool item i, in JVM format (e.g., I, Z, or
* Ljava/lang/Object;)
*/
public String getCPNATType(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_NameAndType) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a NameAndType");
}
try {
return getCPUtf8(getUShort(offset + 3));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid NameAndType type at constant pool item #" + i + ": " + ex.getMessage());
}
}
/**
* @return the name part of the MethodHandle at constant pool item i, in JVM format (e.g., I, Z,
* or Ljava/lang/Object;)
*/
public String getCPHandleName(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_MethodHandle) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a MethodHandle");
}
try {
return getCPRefName(getUShort(offset + 2));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid NameAndType type at constant pool item #" + i + ": " + ex.getMessage());
}
}
/**
* @return the name part of the MethodHandle at constant pool item i, in JVM format (e.g., I, Z,
* or Ljava/lang/Object;)
*/
public String getCPHandleType(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_MethodHandle) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a MethodHandle");
}
try {
return getCPRefType(getUShort(offset + 2));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid NameAndType type at constant pool item #" + i + ": " + ex.getMessage());
}
}
/**
* @return the name part of the MethodHandle at constant pool item i, in JVM format (e.g., I, Z,
* or Ljava/lang/Object;)
*/
public String getCPHandleClass(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_MethodHandle) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a MethodHandle");
}
try {
return getCPRefClass(getUShort(offset + 2));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid NameAndType type at constant pool item #" + i + ": " + ex.getMessage());
}
}
/**
* @return the type of the MethodHandle at constant pool item i
*/
public byte getCPHandleKind(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_MethodHandle) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a MethodHandle");
}
try {
return getByte(offset + 1);
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid NameAndType type at constant pool item #" + i + ": " + ex.getMessage());
}
}
/**
* @return the value of the Integer at constant pool item i
*/
public int getCPInt(int i) throws IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_Integer) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not an Integer");
}
return getInt(offset + 1);
}
/**
* @return the value of the Float at constant pool item i
*/
public float getCPFloat(int i) throws IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_Float) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a Float");
}
return getFloat(offset + 1);
}
/**
* @return the value of the Long at constant pool item i
*/
public long getCPLong(int i) throws IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_Long) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a Long");
}
return getLong(offset + 1);
}
/**
* @return the value of the Double at constant pool item i
*/
public double getCPDouble(int i) throws IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_Double) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a Double");
}
return getDouble(offset + 1);
}
/**
* @return the BootstrapMethodTable index of the bootstrap method for this invokedynamic
*/
public BootstrapMethod getCPDynBootstrap(int i)
throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_InvokeDynamic) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not an InvokeDynamic");
}
try {
int index = getUShort(offset + 1);
return getBootstrapReader().getEntry(index);
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid Ref class at constant pool item #" + i + ": " + ex.getMessage());
}
}
public String getCPDynName(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_InvokeDynamic) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not an InvokeDynamic");
}
try {
return getCPNATName(getUShort(offset + 3));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid Ref class at constant pool item #" + i + ": " + ex.getMessage());
}
}
public String getCPDynType(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_InvokeDynamic) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not an InvokeDynamic");
}
try {
return getCPNATType(getUShort(offset + 3));
} catch (IllegalArgumentException ex) {
throw new InvalidClassFileException(
offset, "Invalid Ref class at constant pool item #" + i + ": " + ex.getMessage());
}
}
private InvalidClassFileException invalidUtf8(int item, int offset) {
return new InvalidClassFileException(
offset,
"Constant pool item #"
+ item
+ " starting at "
+ cpOffsets[item]
+ ", is an invalid Java Utf8 string (byte is "
+ getByte(offset)
+ ')');
}
/**
* @return the value of the Utf8 string at constant pool item i
*/
public String getCPUtf8(int i) throws InvalidClassFileException, IllegalArgumentException {
if (i < 1 || i >= cpItems.length) {
throw new IllegalArgumentException("Constant pool item #" + i + " out of range");
}
int offset = cpOffsets[i];
if (offset == 0 || getByte(offset) != CONSTANT_Utf8) {
throw new IllegalArgumentException("Constant pool item #" + i + " is not a Utf8");
}
String s = cpItems[i];
if (s == null) {
int count = getUShort(offset + 1);
int end = count + offset + 3;
StringBuilder buf = new StringBuilder(count);
offset += 3;
while (offset < end) {
byte x = getByte(offset);
if ((x & 0x80) == 0) {
if (x == 0) {
throw invalidUtf8(i, offset);
}
buf.append((char) x);
offset++;
} else if ((x & 0xE0) == 0xC0) {
if (offset + 1 >= end) {
throw invalidUtf8(i, offset);
}
byte y = getByte(offset + 1);
if ((y & 0xC0) != 0x80) {
throw invalidUtf8(i, offset);
}
buf.append((char) (((x & 0x1F) << 6) + (y & 0x3F)));
offset += 2;
} else if ((x & 0xF0) == 0xE0) {
if (offset + 2 >= end) {
throw invalidUtf8(i, offset);
}
byte y = getByte(offset + 1);
byte z = getByte(offset + 2);
if ((y & 0xC0) != 0x80 || (z & 0xC0) != 0x80) {
throw invalidUtf8(i, offset);
}
buf.append((char) (((x & 0x0F) << 12) + ((y & 0x3F) << 6) + (z & 0x3F)));
offset += 3;
} else {
throw invalidUtf8(i, offset);
}
}
// s = buf.toString().intern(); // removed intern() call --MS
s = buf.toString();
cpItems[i] = s;
}
return s;
}
private void parseConstantPool(int offset, int itemCount) throws InvalidClassFileException {
cpOffsets = new int[itemCount + 1];
cpItems = new String[itemCount];
for (int i = 1; i < itemCount; i++) {
cpOffsets[i] = offset;
byte tag = getByte(offset);
int itemLen;
switch (tag) {
case CONSTANT_String:
case CONSTANT_Class:
case CONSTANT_MethodType:
case CONSTANT_Module:
case CONSTANT_Package:
itemLen = 2;
break;
case CONSTANT_NameAndType:
case CONSTANT_MethodRef:
case CONSTANT_FieldRef:
case CONSTANT_InterfaceMethodRef:
case CONSTANT_Integer:
case CONSTANT_Float:
case CONSTANT_InvokeDynamic:
itemLen = 4;
break;
case CONSTANT_Long:
case CONSTANT_Double:
itemLen = 8;
i++; // ick
break;
case CONSTANT_Utf8:
itemLen = 2 + getUShort(offset + 1);
break;
case CONSTANT_MethodHandle:
itemLen = 3;
break;
default:
throw new InvalidClassFileException(offset, "unknown constant pool entry type" + tag);
}
checkLength(offset, itemLen);
offset += itemLen + 1;
}
cpOffsets[itemCount] = offset;
}
private byte getByte(int i) {
return bytes[i];
}
private int getUShort(int i) {
return ((bytes[i] & 0xFF) << 8) + (bytes[i + 1] & 0xFF);
}
// private short getShort(int i) {
// return (short) ((bytes[i] << 8) + (bytes[i + 1] & 0xFF));
// }
private int getInt(int i) {
return (bytes[i] << 24)
+ ((bytes[i + 1] & 0xFF) << 16)
+ ((bytes[i + 2] & 0xFF) << 8)
+ (bytes[i + 3] & 0xFF);
}
private long getLong(int i) {
return ((long) getInt(i) << 32) + (getInt(i + 4) & 0xFFFFFFFFL);
}
private float getFloat(int i) {
return Float.intBitsToFloat(getInt(i));
}
private double getDouble(int i) {
return Double.longBitsToDouble(getLong(i));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy