jdk/src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java
2024-09-26 06:34:18 +00:00

344 lines
13 KiB
Java

/*
* Copyright (c) 2007, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 com.sun.tools.javap;
import java.lang.classfile.constantpool.*;
import static java.lang.classfile.constantpool.PoolEntry.*;
/*
* Write a constant pool entry.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class ConstantWriter extends BasicWriter {
public static ConstantWriter instance(Context context) {
ConstantWriter instance = context.get(ConstantWriter.class);
if (instance == null)
instance = new ConstantWriter(context);
return instance;
}
protected ConstantWriter(Context context) {
super(context);
context.put(ConstantWriter.class, this);
classWriter = ClassWriter.instance(context);
options = Options.instance(context);
}
protected void writeConstantPool() {
var constant_pool = classWriter.getClassModel().constantPool();
writeConstantPool(constant_pool);
}
protected void writeConstantPool(ConstantPool constant_pool) {
println("Constant pool:");
indent(+1);
int width = String.valueOf(constant_pool.size()).length() + 1;
int cpx = 1;
while (cpx < constant_pool.size()) {
print(String.format("%" + width + "s", ("#" + cpx)));
try {
var cpInfo = constant_pool.entryByIndex(cpx);
print(String.format(" = %-18s ", cpTagName(cpInfo.tag())));
switch (cpInfo) {
case ClassEntry info -> {
print(() -> "#" + info.name().index());
tab();
println(() -> "// " + stringValue(info));
}
case AnnotationConstantValueEntry info -> {
println(() -> stringValue(info));
}
case MemberRefEntry info -> {
print(() -> "#" + info.owner().index() + ".#"
+ info.nameAndType().index());
tab();
println(() -> "// " + stringValue(info));
}
case DynamicConstantPoolEntry info -> {
print(() -> "#" + info.bootstrapMethodIndex() + ":#"
+ info.nameAndType().index());
tab();
println(() -> "// " + stringValue(info));
}
case MethodHandleEntry info -> {
print(() -> info.kind() + ":#" + info.reference().index());
tab();
println(() -> "// " + stringValue(info));
}
case MethodTypeEntry info -> {
print(() -> "#" + info.descriptor().index());
tab();
println(() -> "// " + stringValue(info));
}
case ModuleEntry info -> {
print(() -> "#" + info.name().index());
tab();
println(() -> "// " + stringValue(info));
}
case NameAndTypeEntry info -> {
print(() -> "#" + info.name().index() + ":#" + info.type().index());
tab();
println(() -> "// " + stringValue(info));
}
case PackageEntry info -> {
print(() -> "#" + info.name().index());
tab();
println("// " + stringValue(info));
}
case StringEntry info -> {
print(() -> "#" + info.utf8().index());
tab();
println(() -> "// " + stringValue(info));
}
default ->
throw new IllegalArgumentException("unknown entry: "+ cpInfo);
}
cpx += cpInfo.width();
} catch (IllegalArgumentException e) {
println(report(e));
cpx++;
}
}
indent(-1);
}
protected void write(int cpx) {
if (cpx == 0) {
print("#0");
return;
}
var classModel = classWriter.getClassModel();
var cpInfo = classModel.constantPool().entryByIndex(cpx);
var tag = cpInfo.tag();
if (cpInfo instanceof MemberRefEntry ref) {
// simplify references within this class
if (ref.owner().index() == classModel.thisClass().index())
cpInfo = ref.nameAndType();
}
print(tagName(tag) + " " + stringValue(cpInfo));
}
String cpTagName(int tag) {
return switch (tag) {
case TAG_UTF8 -> "Utf8";
case TAG_INTEGER -> "Integer";
case TAG_FLOAT -> "Float";
case TAG_LONG -> "Long";
case TAG_DOUBLE -> "Double";
case TAG_CLASS -> "Class";
case TAG_STRING -> "String";
case TAG_FIELDREF -> "Fieldref";
case TAG_METHOD_HANDLE -> "MethodHandle";
case TAG_METHOD_TYPE -> "MethodType";
case TAG_METHODREF -> "Methodref";
case TAG_INTERFACE_METHODREF -> "InterfaceMethodref";
case TAG_INVOKE_DYNAMIC -> "InvokeDynamic";
case TAG_DYNAMIC -> "Dynamic";
case TAG_NAME_AND_TYPE -> "NameAndType";
default -> "Unknown";
};
}
String tagName(int tag) {
return switch (tag) {
case TAG_UTF8 -> "Utf8";
case TAG_INTEGER -> "int";
case TAG_FLOAT -> "float";
case TAG_LONG -> "long";
case TAG_DOUBLE -> "double";
case TAG_CLASS -> "class";
case TAG_STRING -> "String";
case TAG_FIELDREF -> "Field";
case TAG_METHOD_HANDLE -> "MethodHandle";
case TAG_METHOD_TYPE -> "MethodType";
case TAG_METHODREF -> "Method";
case TAG_INTERFACE_METHODREF -> "InterfaceMethod";
case TAG_INVOKE_DYNAMIC -> "InvokeDynamic";
case TAG_DYNAMIC -> "Dynamic";
case TAG_NAME_AND_TYPE -> "NameAndType";
default -> "(unknown tag " + tag + ")";
};
}
String booleanValue(PoolEntry info) {
if (info instanceof IntegerEntry ie) {
switch (ie.intValue()) {
case 0: return "false";
case 1: return "true";
}
}
return "#" + info.index();
}
String booleanValue(int constant_pool_index) {
var info = classWriter.getClassModel().constantPool()
.entryByIndex(constant_pool_index);
if (info instanceof IntegerEntry ie) {
switch (ie.intValue()) {
case 0: return "false";
case 1: return "true";
}
}
return "#" + constant_pool_index;
}
String charValue(PoolEntry info) {
if (info instanceof IntegerEntry ie) {
int value = ie.intValue();
return String.valueOf((char) value);
} else {
return "#" + info.index();
}
}
String charValue(int constant_pool_index) {
var info = classWriter.getClassModel().constantPool()
.entryByIndex(constant_pool_index);
if (info instanceof IntegerEntry ie) {
int value = ie.intValue();
return String.valueOf((char) value);
} else {
return "#" + constant_pool_index;
}
}
String stringValue(int constant_pool_index) {
return stringValue(classWriter.getClassModel().constantPool()
.entryByIndex(constant_pool_index));
}
String stringValue(PoolEntry cpInfo) {
return switch (cpInfo) {
case ClassEntry info -> checkName(info.asInternalName());
case DoubleEntry info -> info.doubleValue() + "d";
case MemberRefEntry info -> checkName(info.owner().asInternalName())
+ '.' + stringValue(info.nameAndType());
case FloatEntry info -> info.floatValue()+ "f";
case IntegerEntry info -> String.valueOf(info.intValue());
case DynamicConstantPoolEntry info -> "#" + info.bootstrapMethodIndex()
+ ":" + stringValue(info.nameAndType());
case LongEntry info -> info.longValue()+ "l";
case ModuleEntry info -> checkName(info.name().stringValue());
case NameAndTypeEntry info -> checkName(info.name().stringValue())
+ ':' + info.type().stringValue();
case PackageEntry info -> checkName(info.name().stringValue());
case MethodHandleEntry info -> {
String kind = switch (info.asSymbol().kind()) {
case STATIC, INTERFACE_STATIC -> "REF_invokeStatic";
case VIRTUAL -> "REF_invokeVirtual";
case INTERFACE_VIRTUAL -> "REF_invokeInterface";
case SPECIAL, INTERFACE_SPECIAL -> "REF_invokeSpecial";
case CONSTRUCTOR -> "REF_newInvokeSpecial";
case GETTER -> "REF_getField";
case SETTER -> "REF_putField";
case STATIC_GETTER -> "REF_getStatic";
case STATIC_SETTER -> "REF_putStatic";
};
yield kind + " " + stringValue(info.reference());
}
case MethodTypeEntry info -> info.descriptor().stringValue();
case StringEntry info -> stringValue(info.utf8());
case Utf8Entry info -> {
StringBuilder sb = new StringBuilder();
for (char c : info.stringValue().toCharArray()) {
sb.append(switch (c) {
case '\t' -> "\\t";
case '\n' -> "\\n";
case '\r' -> "\\r";
case '\b' -> "\\b";
case '\f' -> "\\f";
case '\"' -> "\\\"";
case '\'' -> "\\\'";
case '\\' -> "\\\\";
default -> Character.isISOControl(c)
? String.format("\\u%04x", (int) c) : c;
});
}
yield sb.toString();
}
default -> throw new IllegalArgumentException("unknown " + cpInfo);
};
}
/* If name is a valid binary name, return it; otherwise quote it. */
private static String checkName(String name) {
if (name == null)
return "null";
int len = name.length();
if (len == 0)
return "\"\"";
int cc = '/';
int cp;
for (int k = 0; k < len; k += Character.charCount(cp)) {
cp = name.codePointAt(k);
if ((cc == '/' && !Character.isJavaIdentifierStart(cp))
|| (cp != '/' && !Character.isJavaIdentifierPart(cp))) {
return "\"" + addEscapes(name) + "\"";
}
cc = cp;
}
return name;
}
/* If name requires escapes, put them in, so it can be a string body. */
private static String addEscapes(String name) {
String esc = "\\\"\n\t";
String rep = "\\\"nt";
StringBuilder buf = null;
int nextk = 0;
int len = name.length();
for (int k = 0; k < len; k++) {
char cp = name.charAt(k);
int n = esc.indexOf(cp);
if (n >= 0) {
if (buf == null)
buf = new StringBuilder(len * 2);
if (nextk < k)
buf.append(name, nextk, k);
buf.append('\\');
buf.append(rep.charAt(n));
nextk = k+1;
}
}
if (buf == null)
return name;
if (nextk < len)
buf.append(name, nextk, len);
return buf.toString();
}
private final ClassWriter classWriter;
private final Options options;
}