1067 lines
32 KiB
Java
1067 lines
32 KiB
Java
/*
|
|
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
package org.openjdk.asmtools.jdis;
|
|
|
|
import org.openjdk.asmtools.asmutils.HexUtils;
|
|
import org.openjdk.asmtools.asmutils.StringUtils;
|
|
|
|
import java.io.DataInputStream;
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Hashtable;
|
|
import java.util.stream.Collectors;
|
|
|
|
import static java.lang.String.format;
|
|
import static org.openjdk.asmtools.jdis.Utils.commentString;
|
|
|
|
/**
|
|
*
|
|
* ConstantPool
|
|
*
|
|
* Class representing the ConstantPool
|
|
*/
|
|
public class ConstantPool {
|
|
|
|
private static final Hashtable<Byte, TAG> taghash = new Hashtable<>();
|
|
private static final Hashtable<Byte, SUBTAG> subtaghash = new Hashtable<>();
|
|
|
|
private boolean printTAG = false;
|
|
|
|
public void setPrintTAG(boolean value) {
|
|
this.printTAG = value;
|
|
}
|
|
|
|
public String getPrintedTAG(TAG tag) {
|
|
return (this.printTAG) ? tag.tagname + " " : "" ;
|
|
}
|
|
|
|
class Indent {
|
|
private int length, offset, step;
|
|
|
|
void inc() { length+=step; }
|
|
|
|
void dec() { length-=step; }
|
|
|
|
Indent(int offset, int step) {
|
|
this.length = 0;
|
|
this.step = step;
|
|
this.offset = offset;
|
|
}
|
|
|
|
int size() { return offset + length; }
|
|
|
|
/**
|
|
* Creates indent string based on current indent size.
|
|
*/
|
|
private String get() {
|
|
return Collections.nCopies(size(), "\t").stream().collect(Collectors.joining());
|
|
}
|
|
}
|
|
|
|
private final Indent indent = new Indent(2, 1);
|
|
|
|
/**
|
|
* TAG
|
|
*
|
|
* A Tag descriptor of constants in the constant pool
|
|
*
|
|
*/
|
|
public enum TAG {
|
|
CONSTANT_UTF8 ((byte) 1, "Asciz", "CONSTANT_UTF8"),
|
|
CONSTANT_UNICODE ((byte) 2, "unicorn", "CONSTANT_UNICODE"),
|
|
CONSTANT_INTEGER ((byte) 3, "int", "CONSTANT_INTEGER"),
|
|
CONSTANT_FLOAT ((byte) 4, "float", "CONSTANT_FLOAT"),
|
|
CONSTANT_LONG ((byte) 5, "long", "CONSTANT_LONG"),
|
|
CONSTANT_DOUBLE ((byte) 6, "double", "CONSTANT_DOUBLE"),
|
|
CONSTANT_CLASS ((byte) 7, "class", "CONSTANT_CLASS"),
|
|
CONSTANT_STRING ((byte) 8, "String", "CONSTANT_STRING"),
|
|
CONSTANT_FIELD ((byte) 9, "Field", "CONSTANT_FIELD"),
|
|
CONSTANT_METHOD ((byte) 10, "Method", "CONSTANT_METHOD"),
|
|
CONSTANT_INTERFACEMETHOD ((byte) 11, "InterfaceMethod", "CONSTANT_INTERFACEMETHOD"),
|
|
CONSTANT_NAMEANDTYPE ((byte) 12, "NameAndType", "CONSTANT_NAMEANDTYPE"),
|
|
CONSTANT_METHODHANDLE ((byte) 15, "MethodHandle", "CONSTANT_METHODHANDLE"),
|
|
CONSTANT_METHODTYPE ((byte) 16, "MethodType", "CONSTANT_METHODTYPE"),
|
|
CONSTANT_DYNAMIC ((byte) 17, "Dynamic", "CONSTANT_DYNAMIC"),
|
|
CONSTANT_INVOKEDYNAMIC ((byte) 18, "InvokeDynamic", "CONSTANT_INVOKEDYNAMIC"),
|
|
CONSTANT_MODULE ((byte) 19, "Module", "CONSTANT_MODULE"),
|
|
CONSTANT_PACKAGE ((byte) 20, "Package", "CONSTANT_PACKAGE");
|
|
|
|
private final Byte value;
|
|
private final String tagname;
|
|
private final String printval;
|
|
|
|
TAG(byte val, String tgname, String print) {
|
|
value = val;
|
|
tagname = tgname;
|
|
printval = print;
|
|
}
|
|
|
|
public byte value() {
|
|
return value;
|
|
}
|
|
|
|
public String tagname() {
|
|
return tagname;
|
|
}
|
|
|
|
public String description() {
|
|
return printval;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "<" + tagname + "> ";
|
|
}
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
* SUBTAG
|
|
*
|
|
* A Tag descriptor of form method-handle constants
|
|
*
|
|
*/
|
|
static public enum SUBTAG {
|
|
REF_GETFIELD ((byte) 1, "REF_getField", "REF_GETFIELD"),
|
|
REF_GETSTATIC ((byte) 2, "REF_getStatic", "REF_GETSTATIC"),
|
|
REF_PUTFIELD ((byte) 3, "REF_putField", "REF_PUTFIELD"),
|
|
REF_PUTSTATIC ((byte) 4, "REF_putStatic", "REF_PUTSTATIC"),
|
|
REF_INVOKEVIRTUAL ((byte) 5, "REF_invokeVirtual", "REF_INVOKEVIRTUAL"),
|
|
REF_INVOKESTATIC ((byte) 6, "REF_invokeStatic", "REF_INVOKESTATIC"),
|
|
REF_INVOKESPECIAL ((byte) 7, "REF_invokeSpecial", "REF_INVOKESPECIAL"),
|
|
REF_NEWINVOKESPECIAL ((byte) 8, "REF_newInvokeSpecial", "REF_NEWINVOKESPECIAL"),
|
|
REF_INVOKEINTERFACE ((byte) 9, "REF_invokeInterface", "REF_INVOKEINTERFACE");
|
|
|
|
private final Byte value;
|
|
private final String tagname;
|
|
private final String printval;
|
|
|
|
SUBTAG(byte val, String tgname, String print) {
|
|
value = val;
|
|
tagname = tgname;
|
|
printval = print;
|
|
// subtaghash.put(new Byte(val), this);
|
|
}
|
|
|
|
public byte value() {
|
|
return value;
|
|
}
|
|
|
|
public String tagname() {
|
|
return tagname;
|
|
}
|
|
|
|
public String description() {
|
|
return printval;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "<" + tagname + "> ";
|
|
}
|
|
};
|
|
|
|
static {
|
|
|
|
// Class initializer Code
|
|
//
|
|
// Make sure all of the tags get initialized before being used.
|
|
taghash.put(TAG.CONSTANT_UTF8.value(), TAG.CONSTANT_UTF8);
|
|
taghash.put(TAG.CONSTANT_UNICODE.value(), TAG.CONSTANT_UNICODE);
|
|
taghash.put(TAG.CONSTANT_INTEGER.value(), TAG.CONSTANT_INTEGER);
|
|
taghash.put(TAG.CONSTANT_FLOAT.value(), TAG.CONSTANT_FLOAT);
|
|
taghash.put(TAG.CONSTANT_LONG.value(), TAG.CONSTANT_LONG);
|
|
taghash.put(TAG.CONSTANT_DOUBLE.value(), TAG.CONSTANT_DOUBLE);
|
|
taghash.put(TAG.CONSTANT_CLASS.value(), TAG.CONSTANT_CLASS);
|
|
taghash.put(TAG.CONSTANT_STRING.value(), TAG.CONSTANT_STRING);
|
|
taghash.put(TAG.CONSTANT_FIELD.value(), TAG.CONSTANT_FIELD);
|
|
taghash.put(TAG.CONSTANT_METHOD.value(), TAG.CONSTANT_METHOD);
|
|
taghash.put(TAG.CONSTANT_INTERFACEMETHOD.value(), TAG.CONSTANT_INTERFACEMETHOD);
|
|
taghash.put(TAG.CONSTANT_NAMEANDTYPE.value(), TAG.CONSTANT_NAMEANDTYPE);
|
|
taghash.put(TAG.CONSTANT_METHODHANDLE.value(), TAG.CONSTANT_METHODHANDLE);
|
|
taghash.put(TAG.CONSTANT_METHODTYPE.value(), TAG.CONSTANT_METHODTYPE);
|
|
taghash.put(TAG.CONSTANT_DYNAMIC.value(), TAG.CONSTANT_DYNAMIC);
|
|
taghash.put(TAG.CONSTANT_INVOKEDYNAMIC.value(), TAG.CONSTANT_INVOKEDYNAMIC);
|
|
taghash.put(TAG.CONSTANT_MODULE.value(), TAG.CONSTANT_MODULE);
|
|
taghash.put(TAG.CONSTANT_PACKAGE.value(), TAG.CONSTANT_PACKAGE);
|
|
|
|
subtaghash.put(SUBTAG.REF_GETFIELD.value(), SUBTAG.REF_GETFIELD);
|
|
subtaghash.put(SUBTAG.REF_GETSTATIC.value(), SUBTAG.REF_GETSTATIC);
|
|
subtaghash.put(SUBTAG.REF_PUTFIELD.value(), SUBTAG.REF_PUTFIELD);
|
|
subtaghash.put(SUBTAG.REF_PUTSTATIC.value(), SUBTAG.REF_PUTSTATIC);
|
|
subtaghash.put(SUBTAG.REF_INVOKEVIRTUAL.value(), SUBTAG.REF_INVOKEVIRTUAL);
|
|
subtaghash.put(SUBTAG.REF_INVOKESTATIC.value(), SUBTAG.REF_INVOKESTATIC);
|
|
subtaghash.put(SUBTAG.REF_INVOKESPECIAL.value(), SUBTAG.REF_INVOKESPECIAL);
|
|
subtaghash.put(SUBTAG.REF_NEWINVOKESPECIAL.value(), SUBTAG.REF_NEWINVOKESPECIAL);
|
|
subtaghash.put(SUBTAG.REF_INVOKEINTERFACE.value(), SUBTAG.REF_INVOKEINTERFACE);
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Constant
|
|
*
|
|
* Base class of all constant entries
|
|
*
|
|
*/
|
|
public class Constant {
|
|
|
|
/**
|
|
* tag the descriptor for the constant
|
|
*/
|
|
public TAG tag;
|
|
|
|
public Constant(TAG tagval) {
|
|
tag = tagval;
|
|
}
|
|
|
|
public String stringVal() {
|
|
return "";
|
|
}
|
|
|
|
public void print(PrintWriter out) {
|
|
out.print(tag.tagname + "\t");
|
|
}
|
|
|
|
public int size() {
|
|
return 1;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "<CONSTANT " + tag.toString() + " " + stringVal() + ">";
|
|
}
|
|
|
|
private IOException issue;
|
|
|
|
public IOException getIssue() {
|
|
return issue;
|
|
}
|
|
|
|
public void setIssue(IOException value) {
|
|
issue = value;
|
|
}
|
|
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
/* Constant Sub-classes */
|
|
/**
|
|
*
|
|
* CP_Str
|
|
*
|
|
* Constant entries that contain String data. usually is a CONSTANT_UTF8
|
|
*
|
|
*/
|
|
class CP_Str extends Constant {
|
|
|
|
String value;
|
|
|
|
CP_Str(TAG tagval, String str) {
|
|
super(tagval);
|
|
this.value = str;
|
|
}
|
|
|
|
@Override
|
|
public String stringVal() {
|
|
return StringUtils.Utf8ToString(value);
|
|
}
|
|
|
|
@Override
|
|
public void print(PrintWriter out) {
|
|
super.print(out);
|
|
out.println(stringVal() + ";");
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* CP_Int
|
|
*
|
|
* Constant entries that contain Integer data. usually is a CONSTANT_INTEGER
|
|
*
|
|
*/
|
|
class CP_Int extends Constant {
|
|
|
|
Integer value;
|
|
|
|
CP_Int(TAG tagval, int intval) {
|
|
super(tagval);
|
|
this.value = intval;
|
|
}
|
|
|
|
@Override
|
|
public String stringVal() {
|
|
if (cd.options.contains(Options.PR.HEX)) {
|
|
return HexUtils.toHex(value.intValue());
|
|
}
|
|
return value.toString();
|
|
}
|
|
|
|
@Override
|
|
public void print(PrintWriter out) {
|
|
super.print(out);
|
|
out.println(stringVal() + ";");
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* CP_Long
|
|
*
|
|
* Constant entries that contain LongInteger data. usually is a CONSTANT_LONG
|
|
*
|
|
* These take up 2 slots in the constant pool.
|
|
*
|
|
*/
|
|
class CP_Long extends Constant {
|
|
|
|
Long value;
|
|
|
|
CP_Long(TAG tagval, long intval) {
|
|
super(tagval);
|
|
this.value = intval;
|
|
}
|
|
|
|
@Override
|
|
public String stringVal() {
|
|
if (cd.options.contains(Options.PR.HEX)) {
|
|
return HexUtils.toHex(value.longValue()) + 'l';
|
|
}
|
|
return value.toString() + 'l';
|
|
}
|
|
|
|
@Override
|
|
public void print(PrintWriter out) {
|
|
super.print(out);
|
|
out.println(stringVal() + ";");
|
|
}
|
|
|
|
@Override
|
|
public int size() {
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* CP_Float
|
|
*
|
|
* Constant entries that contain Float data. usually is a CONSTANT_FLOAT
|
|
*
|
|
*/
|
|
class CP_Float extends Constant {
|
|
|
|
Float value;
|
|
|
|
CP_Float(TAG tagval, float fltvl) {
|
|
super(tagval);
|
|
this.value = fltvl;
|
|
}
|
|
|
|
@Override
|
|
public String stringVal() {
|
|
if (cd.options.contains(Options.PR.HEX)) {
|
|
return "bits " + HexUtils.toHex(Float.floatToIntBits(value.floatValue()));
|
|
}
|
|
String sf = (value).toString();
|
|
if (value.isNaN() || value.isInfinite()) {
|
|
return sf;
|
|
}
|
|
return sf + "f";
|
|
}
|
|
|
|
@Override
|
|
public void print(PrintWriter out) {
|
|
super.print(out);
|
|
out.println(stringVal() + ";");
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* CP_Double
|
|
*
|
|
* Constant entries that contain double-precision float data. usually is a
|
|
* CONSTANT_DOUBLE
|
|
*
|
|
* These take up 2 slots in the constant pool.
|
|
*
|
|
*/
|
|
class CP_Double extends Constant {
|
|
|
|
Double value;
|
|
|
|
CP_Double(TAG tagval, double fltvl) {
|
|
super(tagval);
|
|
this.value = fltvl;
|
|
}
|
|
|
|
@Override
|
|
public String stringVal() {
|
|
if (cd.options.contains(Options.PR.HEX)) {
|
|
return "bits " + HexUtils.toHex(Double.doubleToLongBits(value.doubleValue())) + 'l';
|
|
}
|
|
String sd = value.toString();
|
|
if (value.isNaN() || value.isInfinite()) {
|
|
return sd;
|
|
}
|
|
return sd + "d";
|
|
}
|
|
|
|
@Override
|
|
public void print(PrintWriter out) {
|
|
super.print(out);
|
|
out.println(stringVal() + ";");
|
|
}
|
|
|
|
@Override
|
|
public int size() {
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* CPX
|
|
*
|
|
* Constant entries that contain a single constant-pool index. Usually, this includes:
|
|
* CONSTANT_CLASS CONSTANT_METHODTYPE CONSTANT_STRING CONSTANT_MODULE CONSTANT_PACKAGE
|
|
*
|
|
*/
|
|
class CPX extends Constant {
|
|
|
|
int value;
|
|
|
|
CPX(TAG tagval, int cpx) {
|
|
super(tagval);
|
|
this.value = cpx;
|
|
}
|
|
|
|
@Override
|
|
public String stringVal() {
|
|
String str = "UnknownTag";
|
|
switch (tag) {
|
|
case CONSTANT_CLASS:
|
|
str = getShortClassName(getClassName(this), cd.pkgPrefix);
|
|
break;
|
|
case CONSTANT_PACKAGE:
|
|
case CONSTANT_MODULE:
|
|
str = getString(value);
|
|
break;
|
|
case CONSTANT_METHODTYPE:
|
|
case CONSTANT_STRING:
|
|
str = StringValue(value);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
@Override
|
|
public void print(PrintWriter out) {
|
|
super.print(out);
|
|
switch (tag) {
|
|
case CONSTANT_CLASS:
|
|
case CONSTANT_STRING:
|
|
case CONSTANT_METHODTYPE:
|
|
case CONSTANT_PACKAGE:
|
|
case CONSTANT_MODULE:
|
|
out.println("#" + (value) + ";\t// " + stringVal());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* CPX2
|
|
*
|
|
* Constant entries that contain two constant-pool indices. Usually, this includes:
|
|
* CONSTANT_FIELD CONSTANT_METHOD CONSTANT_INTERFACEMETHOD CONSTANT_NAMEANDTYPE
|
|
* CONSTANT_METHODHANDLE CONSTANT_DYNAMIC CONSTANT_INVOKEDYNAMIC
|
|
*
|
|
*/
|
|
class CPX2 extends Constant {
|
|
|
|
int value1, value2;
|
|
|
|
CPX2(TAG tagval, int cpx1, int cpx2) {
|
|
super(tagval);
|
|
this.value1 = cpx1;
|
|
this.value2 = cpx2;
|
|
}
|
|
|
|
@Override
|
|
public String stringVal() {
|
|
|
|
String str = "UnknownTag";
|
|
switch (tag) {
|
|
case CONSTANT_FIELD:
|
|
case CONSTANT_METHOD:
|
|
case CONSTANT_INTERFACEMETHOD:
|
|
str = getPrintedTAG(tag) + getShortClassName(getClassName(value1), cd.pkgPrefix) + "." + StringValue(value2);
|
|
break;
|
|
case CONSTANT_NAMEANDTYPE:
|
|
str = getName(value1) + ":" + StringValue(value2);
|
|
break;
|
|
case CONSTANT_METHODHANDLE:
|
|
str = subtagToString(value1) + ":" + StringValue(value2);
|
|
break;
|
|
case CONSTANT_DYNAMIC:
|
|
case CONSTANT_INVOKEDYNAMIC:
|
|
int bsm_attr_idx = value1;
|
|
int nape_idx = value2;
|
|
BootstrapMethodData bsmData;
|
|
try {
|
|
bsmData = cd.bootstrapMethods.get(bsm_attr_idx);
|
|
} catch (NullPointerException npe) {
|
|
return "<Missing BootstrapMethods attribute>";
|
|
} catch (IndexOutOfBoundsException ioob) {
|
|
return "<Invalid bootstrap method index:" + bsm_attr_idx + ">";
|
|
}
|
|
StringBuilder bsm_args_str = new StringBuilder();
|
|
String offsetParm,offsetBrace;
|
|
int bsm_ref = bsmData.bsm_index;
|
|
int bsm_args_len = bsmData.bsm_args_indexes.size();
|
|
if (bsm_args_len > 0) {
|
|
bsm_args_str.append(" {\n");
|
|
offsetBrace = indent.get();
|
|
indent.inc();
|
|
offsetParm = indent.get();
|
|
for (int i = 0; i < bsm_args_len; i++) {
|
|
int bsm_arg_idx = bsmData.bsm_args_indexes.get(i);
|
|
Constant cnt = pool.get(bsm_arg_idx);
|
|
if (cnt.equals(this)) {
|
|
String s = "circular reference to " + cnt.tag.tagname() + " #" + bsm_arg_idx;
|
|
bsm_args_str.append(offsetParm).append(" <").append(s).append(">");
|
|
cnt.setIssue(new IOException(s));
|
|
} else {
|
|
bsm_args_str.append(offsetParm).append(ConstantStrValue(bsm_arg_idx));
|
|
if (i + 1 < bsm_args_len) {
|
|
bsm_args_str.append(",");
|
|
}
|
|
}
|
|
bsm_args_str.append('\n');
|
|
}
|
|
indent.dec();
|
|
bsm_args_str.append(offsetBrace).append("}");
|
|
}
|
|
str = StringValue(bsm_ref) + ":" + StringValue(nape_idx) + bsm_args_str.toString();
|
|
default:
|
|
break;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
public void print(PrintWriter out) {
|
|
super.print(out);
|
|
switch (tag) {
|
|
case CONSTANT_FIELD:
|
|
case CONSTANT_METHOD:
|
|
case CONSTANT_INTERFACEMETHOD:
|
|
out.println("#" + value1 + ".#" + value2 + ";\t// " + stringVal());
|
|
break;
|
|
case CONSTANT_METHODHANDLE:
|
|
out.println(value1 + ":#" + value2 + ";\t// " + stringVal());
|
|
break;
|
|
case CONSTANT_NAMEANDTYPE:
|
|
out.println("#" + value1 + ":#" + value2 + ";\t// " + stringVal());
|
|
break;
|
|
case CONSTANT_DYNAMIC:
|
|
case CONSTANT_INVOKEDYNAMIC:
|
|
out.println(value1 + ":#" + value2 + ";\t" + commentString(stringVal()));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
public boolean refersClassMember() {
|
|
return tag == TAG.CONSTANT_FIELD || tag == TAG.CONSTANT_METHOD || tag == TAG.CONSTANT_INTERFACEMETHOD;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
/* ConstantPool Fields */
|
|
/**
|
|
* The actual pool of Constants
|
|
*/
|
|
public ArrayList<Constant> pool;
|
|
/**
|
|
* Reference to the class data
|
|
*/
|
|
private ClassData cd;
|
|
|
|
|
|
/* -------------------------------------------------------- */
|
|
/* ConstantPool Methods */
|
|
|
|
/* ConstantPool Constructors */
|
|
public ConstantPool(ClassData cd) {
|
|
pool = null;
|
|
this.cd = cd;
|
|
}
|
|
|
|
public ConstantPool(ClassData cd, int size) {
|
|
pool = new ArrayList<>(size);
|
|
this.cd = cd;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* read
|
|
*
|
|
* decodes a ConstantPool and it's constants from a data stream.
|
|
*
|
|
*/
|
|
void read(DataInputStream in) throws IOException {
|
|
int length = in.readUnsignedShort();
|
|
pool = new ArrayList<>(length);
|
|
pool.add(0, null);
|
|
TraceUtils.traceln("CP len=" + length);
|
|
for (int i = 1; i < length; i++) {
|
|
byte tag = in.readByte();
|
|
TAG tagobj = taghash.get(tag);
|
|
TraceUtils.traceln("CP entry #" + i + " + tagindex=" + tag + " tag=" + tagobj);
|
|
switch (tagobj) {
|
|
case CONSTANT_UTF8:
|
|
pool.add(i, new CP_Str(tagobj, in.readUTF()));
|
|
break;
|
|
case CONSTANT_INTEGER:
|
|
pool.add(i, new CP_Int(tagobj, in.readInt()));
|
|
break;
|
|
case CONSTANT_LONG:
|
|
pool.add(i, new CP_Long(tagobj, in.readLong()));
|
|
// handle null entry to account for Longs taking up 2 CP slots
|
|
i += 1;
|
|
pool.add(null);
|
|
break;
|
|
case CONSTANT_FLOAT:
|
|
pool.add(i, new CP_Float(tagobj, in.readFloat()));
|
|
break;
|
|
case CONSTANT_DOUBLE:
|
|
pool.add(i, new CP_Double(tagobj, in.readDouble()));
|
|
// handle null entry to account for Doubles taking up 2 CP slots
|
|
i += 1;
|
|
pool.add(null);
|
|
break;
|
|
case CONSTANT_CLASS:
|
|
case CONSTANT_STRING:
|
|
case CONSTANT_METHODTYPE:
|
|
case CONSTANT_PACKAGE:
|
|
case CONSTANT_MODULE:
|
|
pool.add(i, new CPX(tagobj, in.readUnsignedShort()));
|
|
break;
|
|
case CONSTANT_FIELD:
|
|
case CONSTANT_METHOD:
|
|
case CONSTANT_INTERFACEMETHOD:
|
|
case CONSTANT_NAMEANDTYPE:
|
|
case CONSTANT_DYNAMIC:
|
|
case CONSTANT_INVOKEDYNAMIC:
|
|
pool.add(i, new CPX2(tagobj, in.readUnsignedShort(), in.readUnsignedShort()));
|
|
break;
|
|
case CONSTANT_METHODHANDLE:
|
|
pool.add(i, new CPX2(tagobj, in.readUnsignedByte(), in.readUnsignedShort()));
|
|
break;
|
|
|
|
default:
|
|
throw new ClassFormatError("invalid constant type: " + (int) tag);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* inbounds
|
|
*
|
|
* bounds-check a CP index.
|
|
*
|
|
*/
|
|
private boolean inbounds(int cpx) {
|
|
return !(cpx == 0 || cpx >= pool.size());
|
|
}
|
|
|
|
/**
|
|
*
|
|
* getConst
|
|
*
|
|
* Public getter - Safely gets a Constant from the CP at a given index.
|
|
*
|
|
*/
|
|
public Constant getConst(int cpx) {
|
|
if (inbounds(cpx)) {
|
|
return pool.get(cpx);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* StringTag
|
|
*
|
|
* Public string val - Safely gets the string-rep of a Constant from the CP at a given
|
|
* index.
|
|
*
|
|
*/
|
|
public String StringTag(int cpx) {
|
|
String str = "Incorrect CP index:" + cpx;
|
|
if (inbounds(cpx)) {
|
|
Constant cns = pool.get(cpx);
|
|
if (cns != null) {
|
|
str = cns.tag.tagname;
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* getString
|
|
*
|
|
* Public string val - Safely gets the string-rep of a ConstantUTF8 from the CP at a
|
|
* given index.
|
|
*
|
|
* Returns either null (if invalid), or the string value of the UTF8
|
|
*
|
|
*/
|
|
public String getString(int cpx) {
|
|
String str = null;
|
|
if (inbounds(cpx)) {
|
|
Constant cns = pool.get(cpx);
|
|
if (cns != null && cns.tag == TAG.CONSTANT_UTF8) {
|
|
CP_Str cns1 = (CP_Str) cns;
|
|
str = cns1.value;
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* getModule
|
|
*
|
|
* Public string val - Safely gets the string-rep of a ConstantModule from the CP at a
|
|
* given index.
|
|
*
|
|
* Returns either null (if invalid), or the string value of the ConstantModule
|
|
*
|
|
*/
|
|
public String getModule(int cpx) {
|
|
String str = null;
|
|
if (inbounds(cpx)) {
|
|
Constant cns = pool.get(cpx);
|
|
if (cns != null && cns.tag == TAG.CONSTANT_MODULE) {
|
|
str = cns.stringVal();
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* getPackage
|
|
*
|
|
* Public string val - Safely gets the string-rep of a ConstantPackage from the CP at a
|
|
* given index.
|
|
*
|
|
* Returns either null (if invalid), or the string value of the ConstantPackage
|
|
*
|
|
*/
|
|
public String getPackage(int cpx) {
|
|
String str = null;
|
|
if (inbounds(cpx)) {
|
|
Constant cns = pool.get(cpx);
|
|
if (cns != null && cns.tag == TAG.CONSTANT_PACKAGE) {
|
|
str = cns.stringVal();
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* getTypeName
|
|
*
|
|
* Safely gets a Java name from a ConstantUTF8 from the CP at a given index.
|
|
*
|
|
* Returns either null (if invalid), or the Java name value of the UTF8
|
|
*
|
|
*/
|
|
public String getName(int cpx) {
|
|
String str = getString(cpx);
|
|
if (str == null) {
|
|
return "<invalid constant pool index:" + cpx + ">";
|
|
}
|
|
|
|
return Utils.javaName(str);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* getClassName
|
|
*
|
|
* Safely gets a Java class name from a ConstantClass from the CP at a given index.
|
|
*
|
|
* Returns either the Java class name, or a CP index reference string.
|
|
*
|
|
*/
|
|
public String getClassName(int cpx) {
|
|
String res = "#" + cpx;
|
|
if (cpx == 0) {
|
|
return res;
|
|
}
|
|
if (!inbounds(cpx)) {
|
|
return res;
|
|
}
|
|
Constant cns = pool.get(cpx);
|
|
if (cns == null || cns.tag != TAG.CONSTANT_CLASS) {
|
|
return res;
|
|
}
|
|
|
|
return getClassName((CPX) cns);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* getClassName
|
|
*
|
|
* Safely gets a Java class name from a ConstantClass from a CPX2 constant pool
|
|
* object. (eg. Method/Field/Interface Ref)
|
|
*
|
|
* Returns either the Java class name, or a CP index reference string.
|
|
*
|
|
*/
|
|
public String getClassName(CPX2 classConst) {
|
|
return _getClassName(classConst.value1);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* getClassName
|
|
*
|
|
* Safely gets a Java class name from a ConstantClass from a CPX constant pool object.
|
|
* (eg. Class Ref)
|
|
*
|
|
* Returns either the Java class name, or a CP index reference string.
|
|
*
|
|
*/
|
|
public String getClassName(CPX classConst) {
|
|
return _getClassName(classConst.value);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* _getClassName
|
|
*
|
|
* Helper for getting class name. Checks bounds, does name conversion.
|
|
*
|
|
*/
|
|
private String _getClassName(int nameIndex) {
|
|
String res = "#" + nameIndex;
|
|
if (!inbounds(nameIndex)) {
|
|
return res;
|
|
}
|
|
Constant nameconst = pool.get(nameIndex);
|
|
if (nameconst == null || nameconst.tag != TAG.CONSTANT_UTF8) {
|
|
return res;
|
|
}
|
|
CP_Str name = (CP_Str) nameconst;
|
|
|
|
String classname = name.value;
|
|
|
|
if (Utils.isClassArrayDescriptor(classname)) {
|
|
classname = "\"" + classname + "\"";
|
|
}
|
|
return classname;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* getShortClassName
|
|
*
|
|
* shortens a class name (if the class is in the given package). works with a
|
|
* string-encoded classname.
|
|
*
|
|
*/
|
|
public String getShortClassName(String className, String pkgPrefix) {
|
|
if (className.startsWith(pkgPrefix)) {
|
|
return className.substring(pkgPrefix.length());
|
|
}
|
|
return className;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* getShortClassName
|
|
*
|
|
* shortens a class name (if the class is in the given package). works with a CP index
|
|
* to a ConstantClass.
|
|
*
|
|
*/
|
|
public String getShortClassName(int cpx, String pkgPrefix) {
|
|
String name = Utils.javaName(getClassName(cpx));
|
|
return getShortClassName(name, pkgPrefix);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* decodeClassDescriptor
|
|
*
|
|
* Pulls the class name out of a string (at the CP index). (drops any array
|
|
* descriptors, and the class descriptors ("L" and ";")
|
|
*
|
|
*/
|
|
public String decodeClassDescriptor(int cpx) {
|
|
// enum type is encoded as a descriptor
|
|
// need to remove '"'s and L (class descriptor)
|
|
|
|
// TODO: might have to count '['s at the beginning for Arrays
|
|
String rawEnumName = getName(cpx);
|
|
int len = rawEnumName.length();
|
|
int begin = (rawEnumName.startsWith("\"L")) ? 2 : 0;
|
|
int end = (begin > 0) ? len - 2 : len;
|
|
return rawEnumName.substring(begin, end);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* subtagToString
|
|
*
|
|
* Getter that safely gets the string descriptor of a subtag
|
|
*
|
|
*/
|
|
private String subtagToString(int subtag) {
|
|
SUBTAG st = subtaghash.get((byte) subtag);
|
|
if (st == null) {
|
|
return "BOGUS_SUBTAG:" + subtag;
|
|
}
|
|
return st.tagname;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* StringValue
|
|
*
|
|
* Safely gets the string value of any Constant at any CP index.
|
|
*
|
|
*/
|
|
public String StringValue(int cpx) {
|
|
if (cpx == 0) {
|
|
return "#0";
|
|
}
|
|
if (!inbounds(cpx)) {
|
|
return "<Incorrect CP index:" + cpx + ">";
|
|
}
|
|
Constant cnst = pool.get(cpx);
|
|
if (cnst == null) {
|
|
return "<NULL>";
|
|
}
|
|
return cnst.stringVal();
|
|
}
|
|
|
|
/**
|
|
* ConstantStrValue
|
|
*
|
|
* Safely gets the string value of any Constant at any CP index. This string is either
|
|
* a Constant's String value, or a CP index reference string. The Constant string has
|
|
* a tag descriptor in the beginning.
|
|
*
|
|
*/
|
|
public String ConstantStrValue(int cpx) {
|
|
if (cpx == 0) {
|
|
return "#0";
|
|
}
|
|
if (!inbounds(cpx)) {
|
|
return "#" + cpx;
|
|
}
|
|
Constant cns = pool.get(cpx);
|
|
if (cns == null) {
|
|
return "#" + cpx;
|
|
}
|
|
if (cns instanceof CPX2) {
|
|
CPX2 cns2 = (CPX2) cns;
|
|
if (cns2.value1 == cd.this_cpx && cns2.refersClassMember()) {
|
|
cpx = cns2.value2;
|
|
}
|
|
}
|
|
return cns.tag.tagname + " " + StringValue(cpx);
|
|
}
|
|
|
|
/**
|
|
* prints the entire constant pool.
|
|
*/
|
|
public void print(PrintWriter out) throws IOException {
|
|
int cpx = 0;
|
|
for (Constant cns : pool) {
|
|
if (cpx == 0) {
|
|
cpx += 1;
|
|
continue;
|
|
}
|
|
|
|
out.print("\tconst #" + cpx + " = ");
|
|
|
|
if (cns == null) {
|
|
// do something
|
|
out.println("null");
|
|
cpx += 1;
|
|
} else {
|
|
cns.print(out);
|
|
cpx += cns.size();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* prints the Constant value at a given CP index.
|
|
*/
|
|
void PrintConstant(PrintWriter out, int cpx) {
|
|
out.print(ConstantStrValue(cpx));
|
|
}
|
|
|
|
/**
|
|
* prints a constant value, with the print format based on the print options.
|
|
*/
|
|
public void printlnClassId(PrintWriter out, int cpx) throws IOException {
|
|
printlnClassId(out, cpx, false);
|
|
}
|
|
|
|
public void printlnClassId(PrintWriter out, int cpx, boolean addComma) throws IOException {
|
|
if (!cd.options.contains(Options.PR.CPX)) {
|
|
out.print(getShortClassName(cpx, cd.pkgPrefix) + (addComma ? "," : ""));
|
|
} else {
|
|
out.print("\t#" + cpx + (addComma ? "," : "") + " //");
|
|
PrintConstant(out, cpx);
|
|
}
|
|
}
|
|
|
|
}
|