/* * 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. * *
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. */ 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; }