8294969: Convert jdk.jdeps javap to use the Classfile API

Reviewed-by: vromero
This commit is contained in:
Adam Sotona 2023-09-18 08:35:31 +00:00
parent fbc766ee21
commit 1203e11a8d
29 changed files with 2012 additions and 2870 deletions

View file

@ -102,6 +102,16 @@ public sealed interface ClassReader extends ConstantPool
*/ */
PoolEntry readEntry(int offset); PoolEntry readEntry(int offset);
/**
* {@return the constant pool entry of a given type whose index is given
* at the specified offset within the classfile}
* @param offset the offset of the index within the classfile
* @param cls the entry type
* @throws ConstantPoolException if the index is out of range of the
* constant pool size, or zero, or the entry is not of the given type
*/
<T extends PoolEntry> T readEntry(int offset, Class<T> cls);
/** /**
* {@return the constant pool entry whose index is given at the specified * {@return the constant pool entry whose index is given at the specified
* offset within the classfile, or null if the index at the specified * offset within the classfile, or null if the index at the specified

View file

@ -38,6 +38,11 @@ public sealed interface DynamicConstantPoolEntry extends PoolEntry
*/ */
BootstrapMethodEntry bootstrap(); BootstrapMethodEntry bootstrap();
/**
* {@return index of the entry in the bootstrap method table for this constant}
*/
int bootstrapMethodIndex();
/** /**
* {@return the invocation name and type} * {@return the invocation name and type}
*/ */

View file

@ -383,7 +383,7 @@ public abstract sealed class AbstractInstruction
@Override @Override
public FieldRefEntry field() { public FieldRefEntry field() {
if (fieldEntry == null) if (fieldEntry == null)
fieldEntry = (FieldRefEntry) code.classReader.readEntry(pos + 1); fieldEntry = code.classReader.readEntry(pos + 1, FieldRefEntry.class);
return fieldEntry; return fieldEntry;
} }
@ -413,7 +413,7 @@ public abstract sealed class AbstractInstruction
@Override @Override
public MemberRefEntry method() { public MemberRefEntry method() {
if (methodEntry == null) if (methodEntry == null)
methodEntry = (MemberRefEntry) code.classReader.readEntry(pos + 1); methodEntry = code.classReader.readEntry(pos + 1, MemberRefEntry.class);
return methodEntry; return methodEntry;
} }
@ -453,7 +453,7 @@ public abstract sealed class AbstractInstruction
@Override @Override
public MemberRefEntry method() { public MemberRefEntry method() {
if (methodEntry == null) if (methodEntry == null)
methodEntry = (InterfaceMethodRefEntry) code.classReader.readEntry(pos + 1); methodEntry = code.classReader.readEntry(pos + 1, InterfaceMethodRefEntry.class);
return methodEntry; return methodEntry;
} }
@ -493,7 +493,7 @@ public abstract sealed class AbstractInstruction
@Override @Override
public InvokeDynamicEntry invokedynamic() { public InvokeDynamicEntry invokedynamic() {
if (indyEntry == null) if (indyEntry == null)
indyEntry = (InvokeDynamicEntry) code.classReader.readEntry(pos + 1); indyEntry = code.classReader.readEntry(pos + 1, InvokeDynamicEntry.class);
return indyEntry; return indyEntry;
} }

View file

@ -815,6 +815,13 @@ public abstract sealed class AbstractPoolEntry {
return bootstrapMethod; return bootstrapMethod;
} }
/**
* @return the bsmIndex
*/
public int bootstrapMethodIndex() {
return bsmIndex;
}
/** /**
* @return the nameAndType * @return the nameAndType
*/ */

View file

@ -58,14 +58,14 @@ class AnnotationReader {
char tag = (char) classReader.readU1(p); char tag = (char) classReader.readU1(p);
++p; ++p;
return switch (tag) { return switch (tag) {
case AEV_BYTE -> new AnnotationImpl.OfByteImpl((IntegerEntry)classReader.readEntry(p)); case AEV_BYTE -> new AnnotationImpl.OfByteImpl(classReader.readEntry(p, IntegerEntry.class));
case AEV_CHAR -> new AnnotationImpl.OfCharacterImpl((IntegerEntry)classReader.readEntry(p)); case AEV_CHAR -> new AnnotationImpl.OfCharacterImpl(classReader.readEntry(p, IntegerEntry.class));
case AEV_DOUBLE -> new AnnotationImpl.OfDoubleImpl((DoubleEntry)classReader.readEntry(p)); case AEV_DOUBLE -> new AnnotationImpl.OfDoubleImpl(classReader.readEntry(p, DoubleEntry.class));
case AEV_FLOAT -> new AnnotationImpl.OfFloatImpl((FloatEntry)classReader.readEntry(p)); case AEV_FLOAT -> new AnnotationImpl.OfFloatImpl(classReader.readEntry(p, FloatEntry.class));
case AEV_INT -> new AnnotationImpl.OfIntegerImpl((IntegerEntry)classReader.readEntry(p)); case AEV_INT -> new AnnotationImpl.OfIntegerImpl(classReader.readEntry(p, IntegerEntry.class));
case AEV_LONG -> new AnnotationImpl.OfLongImpl((LongEntry)classReader.readEntry(p)); case AEV_LONG -> new AnnotationImpl.OfLongImpl(classReader.readEntry(p, LongEntry.class));
case AEV_SHORT -> new AnnotationImpl.OfShortImpl((IntegerEntry)classReader.readEntry(p)); case AEV_SHORT -> new AnnotationImpl.OfShortImpl(classReader.readEntry(p, IntegerEntry.class));
case AEV_BOOLEAN -> new AnnotationImpl.OfBooleanImpl((IntegerEntry)classReader.readEntry(p)); case AEV_BOOLEAN -> new AnnotationImpl.OfBooleanImpl(classReader.readEntry(p, IntegerEntry.class));
case AEV_STRING -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p)); case AEV_STRING -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p));
case AEV_ENUM -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2)); case AEV_ENUM -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2));
case AEV_CLASS -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p)); case AEV_CLASS -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p));

View file

@ -483,7 +483,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
@Override @Override
public ConstantValueEntry constant() { public ConstantValueEntry constant() {
return (ConstantValueEntry) classReader.readEntry(payloadStart); return classReader.readEntry(payloadStart, ConstantValueEntry.class);
} }
} }

View file

@ -27,7 +27,6 @@ package jdk.internal.classfile.impl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
@ -37,10 +36,6 @@ import jdk.internal.classfile.attribute.BootstrapMethodsAttribute;
import jdk.internal.classfile.constantpool.ClassEntry; import jdk.internal.classfile.constantpool.ClassEntry;
import jdk.internal.classfile.constantpool.ConstantPoolException; import jdk.internal.classfile.constantpool.ConstantPoolException;
import jdk.internal.classfile.constantpool.LoadableConstantEntry; import jdk.internal.classfile.constantpool.LoadableConstantEntry;
import jdk.internal.classfile.constantpool.MethodHandleEntry;
import jdk.internal.classfile.constantpool.ModuleEntry;
import jdk.internal.classfile.constantpool.NameAndTypeEntry;
import jdk.internal.classfile.constantpool.PackageEntry;
import jdk.internal.classfile.constantpool.PoolEntry; import jdk.internal.classfile.constantpool.PoolEntry;
import jdk.internal.classfile.constantpool.Utf8Entry; import jdk.internal.classfile.constantpool.Utf8Entry;
@ -61,6 +56,10 @@ import static jdk.internal.classfile.Classfile.TAG_NAMEANDTYPE;
import static jdk.internal.classfile.Classfile.TAG_PACKAGE; import static jdk.internal.classfile.Classfile.TAG_PACKAGE;
import static jdk.internal.classfile.Classfile.TAG_STRING; import static jdk.internal.classfile.Classfile.TAG_STRING;
import static jdk.internal.classfile.Classfile.TAG_UTF8; import static jdk.internal.classfile.Classfile.TAG_UTF8;
import jdk.internal.classfile.constantpool.MethodHandleEntry;
import jdk.internal.classfile.constantpool.ModuleEntry;
import jdk.internal.classfile.constantpool.NameAndTypeEntry;
import jdk.internal.classfile.constantpool.PackageEntry;
public final class ClassReaderImpl public final class ClassReaderImpl
implements ClassReader { implements ClassReader {
@ -156,7 +155,7 @@ public final class ClassReaderImpl
@Override @Override
public ClassEntry thisClassEntry() { public ClassEntry thisClassEntry() {
if (thisClass == null) { if (thisClass == null) {
thisClass = readClassEntry(thisClassPos); thisClass = readEntry(thisClassPos, ClassEntry.class);
} }
return thisClass; return thisClass;
} }
@ -391,6 +390,13 @@ public final class ClassReaderImpl
return entryByIndex(readU2(pos)); return entryByIndex(readU2(pos));
} }
@Override
public <T extends PoolEntry> T readEntry(int pos, Class<T> cls) {
var e = readEntry(pos);
if (cls.isInstance(e)) return cls.cast(e);
throw new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + readU2(pos));
}
@Override @Override
public PoolEntry readEntryOrNull(int pos) { public PoolEntry readEntryOrNull(int pos) {
int index = readU2(pos); int index = readU2(pos);
@ -417,32 +423,27 @@ public final class ClassReaderImpl
@Override @Override
public ModuleEntry readModuleEntry(int pos) { public ModuleEntry readModuleEntry(int pos) {
if (readEntry(pos) instanceof ModuleEntry me) return me; return readEntry(pos, ModuleEntry.class);
throw new ConstantPoolException("Not a module entry at pos: " + pos);
} }
@Override @Override
public PackageEntry readPackageEntry(int pos) { public PackageEntry readPackageEntry(int pos) {
if (readEntry(pos) instanceof PackageEntry pe) return pe; return readEntry(pos, PackageEntry.class);
throw new ConstantPoolException("Not a package entry at pos: " + pos);
} }
@Override @Override
public ClassEntry readClassEntry(int pos) { public ClassEntry readClassEntry(int pos) {
if (readEntry(pos) instanceof ClassEntry ce) return ce; return readEntry(pos, ClassEntry.class);
throw new ConstantPoolException("Not a class entry at pos: " + pos);
} }
@Override @Override
public NameAndTypeEntry readNameAndTypeEntry(int pos) { public NameAndTypeEntry readNameAndTypeEntry(int pos) {
if (readEntry(pos) instanceof NameAndTypeEntry nate) return nate; return readEntry(pos, NameAndTypeEntry.class);
throw new ConstantPoolException("Not a name and type entry at pos: " + pos);
} }
@Override @Override
public MethodHandleEntry readMethodHandleEntry(int pos) { public MethodHandleEntry readMethodHandleEntry(int pos) {
if (readEntry(pos) instanceof MethodHandleEntry mhe) return mhe; return readEntry(pos, MethodHandleEntry.class);
throw new ConstantPoolException("Not a method handle entry at pos: " + pos);
} }
@Override @Override

View file

@ -186,15 +186,19 @@ module java.base {
java.logging; java.logging;
exports jdk.internal.classfile to exports jdk.internal.classfile to
jdk.jartool, jdk.jartool,
jdk.jdeps,
jdk.jlink, jdk.jlink,
jdk.jshell; jdk.jshell;
exports jdk.internal.classfile.attribute to exports jdk.internal.classfile.attribute to
jdk.jartool, jdk.jartool,
jdk.jdeps,
jdk.jlink; jdk.jlink;
exports jdk.internal.classfile.constantpool to exports jdk.internal.classfile.constantpool to
jdk.jartool, jdk.jartool,
jdk.jdeps,
jdk.jlink; jdk.jlink;
exports jdk.internal.classfile.instruction to exports jdk.internal.classfile.instruction to
jdk.jdeps,
jdk.jlink, jdk.jlink,
jdk.jshell; jdk.jshell;
exports jdk.internal.org.objectweb.asm to exports jdk.internal.org.objectweb.asm to

View file

@ -25,17 +25,14 @@
package com.sun.tools.javap; package com.sun.tools.javap;
import com.sun.tools.classfile.Annotation; import java.util.List;
import com.sun.tools.classfile.TypeAnnotation; import jdk.internal.classfile.Annotation;
import com.sun.tools.classfile.Annotation.Annotation_element_value; import jdk.internal.classfile.AnnotationElement;
import com.sun.tools.classfile.Annotation.Array_element_value; import jdk.internal.classfile.AnnotationValue;
import com.sun.tools.classfile.Annotation.Class_element_value; import jdk.internal.classfile.constantpool.*;
import com.sun.tools.classfile.Annotation.Enum_element_value; import jdk.internal.classfile.Signature;
import com.sun.tools.classfile.Annotation.Primitive_element_value; import jdk.internal.classfile.TypeAnnotation;
import com.sun.tools.classfile.ConstantPool; import jdk.internal.classfile.attribute.CodeAttribute;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Descriptor;
import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
/** /**
* A writer for writing annotations as text. * A writer for writing annotations as text.
@ -68,15 +65,15 @@ public class AnnotationWriter extends BasicWriter {
} }
public void write(Annotation annot, boolean resolveIndices) { public void write(Annotation annot, boolean resolveIndices) {
writeDescriptor(annot.type_index, resolveIndices); writeDescriptor(annot.className(), resolveIndices);
if (resolveIndices) { if (resolveIndices) {
boolean showParens = annot.num_element_value_pairs > 0; boolean showParens = annot.elements().size() > 0;
if (showParens) { if (showParens) {
println("("); println("(");
indent(+1); indent(+1);
} }
for (int i = 0; i < annot.num_element_value_pairs; i++) { for (var element : annot.elements()) {
write(annot.element_value_pairs[i], true); write(element, true);
println(); println();
} }
if (showParens) { if (showParens) {
@ -85,143 +82,126 @@ public class AnnotationWriter extends BasicWriter {
} }
} else { } else {
print("("); print("(");
for (int i = 0; i < annot.num_element_value_pairs; i++) { for (int i = 0; i < annot.elements().size(); i++) {
if (i > 0) if (i > 0)
print(","); print(",");
write(annot.element_value_pairs[i], false); write(annot.elements().get(i), false);
} }
print(")"); print(")");
} }
} }
public void write(TypeAnnotation annot) { public void write(TypeAnnotation annot, CodeAttribute lr) {
write(annot, true, false); write(annot, true, false, lr);
println(); println();
indent(+1); indent(+1);
write(annot.annotation, true); write(annot, true);
indent(-1); indent(-1);
} }
public void write(TypeAnnotation annot, boolean showOffsets, boolean resolveIndices) { public void write(TypeAnnotation annot, boolean showOffsets,
write(annot.annotation, resolveIndices); boolean resolveIndices, CodeAttribute lr) {
write(annot, resolveIndices);
print(": "); print(": ");
write(annot.position, showOffsets); write(annot.targetInfo(), annot.targetPath(), showOffsets, lr);
} }
public void write(TypeAnnotation.Position pos, boolean showOffsets) { public void write(TypeAnnotation.TargetInfo targetInfo,
print(pos.type); List<TypeAnnotation.TypePathComponent> targetPath,
boolean showOffsets, CodeAttribute lr) {
print(targetInfo.targetType());
switch (pos.type) { switch (targetInfo) {
// instanceof // instanceof
case INSTANCEOF:
// new expression // new expression
case NEW:
// constructor/method reference receiver // constructor/method reference receiver
case CONSTRUCTOR_REFERENCE: case TypeAnnotation.OffsetTarget pos -> {
case METHOD_REFERENCE:
if (showOffsets) { if (showOffsets) {
print(", offset="); print(", offset=");
print(pos.offset); print(lr.labelToBci(pos.target()));
} }
break; }
// local variable case TypeAnnotation.LocalVarTarget pos -> {
case LOCAL_VARIABLE: if (pos.table().isEmpty()) {
// resource variable
case RESOURCE_VARIABLE:
if (pos.lvarOffset == null) {
print(", lvarOffset is Null!"); print(", lvarOffset is Null!");
break; break;
} }
print(", {"); print(", {");
for (int i = 0; i < pos.lvarOffset.length; ++i) { var table = pos.table();
for (int i = 0; i < table.size(); ++i) {
var e = table.get(i);
if (i != 0) print("; "); if (i != 0) print("; ");
int startPc = lr.labelToBci(e.startLabel());
if (showOffsets) { if (showOffsets) {
print("start_pc="); print("start_pc=");
print(pos.lvarOffset[i]); print(startPc);
} }
print(", length="); print(", length=");
print(pos.lvarLength[i]); print(lr.labelToBci(e.endLabel()) - startPc);
print(", index="); print(", index=");
print(pos.lvarIndex[i]); print(e.index());
} }
print("}"); print("}");
break; }
// exception parameter case TypeAnnotation.CatchTarget pos -> {
case EXCEPTION_PARAMETER:
print(", exception_index="); print(", exception_index=");
print(pos.exception_index); print(pos.exceptionTableIndex());
break; }
// method receiver case TypeAnnotation.TypeParameterTarget pos -> {
case METHOD_RECEIVER:
// Do nothing
break;
// type parameter
case CLASS_TYPE_PARAMETER:
case METHOD_TYPE_PARAMETER:
print(", param_index="); print(", param_index=");
print(pos.parameter_index); print(pos.typeParameterIndex());
break; }
// type parameter bound case TypeAnnotation.TypeParameterBoundTarget pos -> {
case CLASS_TYPE_PARAMETER_BOUND:
case METHOD_TYPE_PARAMETER_BOUND:
print(", param_index="); print(", param_index=");
print(pos.parameter_index); print(pos.typeParameterIndex());
print(", bound_index="); print(", bound_index=");
print(pos.bound_index); print(pos.boundIndex());
break; }
// class extends or implements clause case TypeAnnotation.SupertypeTarget pos -> {
case CLASS_EXTENDS:
print(", type_index="); print(", type_index=");
print(pos.type_index); print(pos.supertypeIndex());
break; }
// throws case TypeAnnotation.ThrowsTarget pos -> {
case THROWS:
print(", type_index="); print(", type_index=");
print(pos.type_index); print(pos.throwsTargetIndex());
break; }
// method parameter case TypeAnnotation.FormalParameterTarget pos -> {
case METHOD_FORMAL_PARAMETER:
print(", param_index="); print(", param_index=");
print(pos.parameter_index); print(pos.formalParameterIndex());
break; }
// type cast case TypeAnnotation.TypeArgumentTarget pos -> {
case CAST:
// method/constructor/reference type argument
case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
case METHOD_INVOCATION_TYPE_ARGUMENT:
case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
case METHOD_REFERENCE_TYPE_ARGUMENT:
if (showOffsets) { if (showOffsets) {
print(", offset="); print(", offset=");
print(pos.offset); print(lr.labelToBci(pos.target()));
} }
print(", type_index="); print(", type_index=");
print(pos.type_index); print(pos.typeArgumentIndex());
break; }
// We don't need to worry about these case TypeAnnotation.EmptyTarget pos -> {
case METHOD_RETURN: // Do nothing
case FIELD: }
break; default ->
case UNKNOWN: throw new AssertionError("AnnotationWriter: Unhandled target type: "
throw new AssertionError("AnnotationWriter: UNKNOWN target type should never occur!"); + targetInfo.getClass());
default:
throw new AssertionError("AnnotationWriter: Unknown target type for position: " + pos);
} }
// Append location data for generics/arrays. // Append location data for generics/arrays.
if (!pos.location.isEmpty()) { if (!targetPath.isEmpty()) {
print(", location="); print(", location=");
print(pos.location); print(targetPath.stream().map(tp -> tp.typePathKind().toString() +
(tp.typePathKind() == TypeAnnotation.TypePathComponent.Kind.TYPE_ARGUMENT
? ("(" + tp.typeArgumentIndex() + ")")
: "")).toList());
} }
} }
public void write(Annotation.element_value_pair pair, boolean resolveIndices) { public void write(AnnotationElement pair, boolean resolveIndices) {
writeIndex(pair.element_name_index, resolveIndices); writeIndex(pair.name(), resolveIndices);
print("="); print("=");
write(pair.value, resolveIndices); write(pair.value(), resolveIndices);
} }
public void write(Annotation.element_value value) { public void write(AnnotationValue value) {
write(value, false); write(value, false);
println(); println();
indent(+1); indent(+1);
@ -229,122 +209,94 @@ public class AnnotationWriter extends BasicWriter {
indent(-1); indent(-1);
} }
public void write(Annotation.element_value value, boolean resolveIndices) { private void writeDescriptor(Utf8Entry entry, boolean resolveIndices) {
ev_writer.write(value, resolveIndices);
}
private void writeDescriptor(int index, boolean resolveIndices) {
if (resolveIndices) { if (resolveIndices) {
try { print(classWriter.sigPrinter.print(Signature.parseFrom(entry.stringValue())));
ConstantPool constant_pool = classWriter.getClassFile().constant_pool;
Descriptor d = new Descriptor(index);
print(d.getFieldType(constant_pool));
return; return;
} catch (ConstantPoolException | InvalidDescriptor ignore) {
} }
print("#" + entry.index());
} }
print("#" + index); private void writeIndex(PoolEntry entry, boolean resolveIndices) {
}
private void writeIndex(int index, boolean resolveIndices) {
if (resolveIndices) { if (resolveIndices) {
print(constantWriter.stringValue(index)); print(constantWriter.stringValue(entry));
} else } else
print("#" + index); print("#" + entry.index());
} }
element_value_Writer ev_writer = new element_value_Writer(); public void write(AnnotationValue value, boolean resolveIndices) {
switch (value) {
class element_value_Writer implements Annotation.element_value.Visitor<Void,Boolean> { case AnnotationValue.OfConstant ev -> {
public void write(Annotation.element_value value, boolean resolveIndices) {
value.accept(this, resolveIndices);
}
@Override
public Void visitPrimitive(Primitive_element_value ev, Boolean resolveIndices) {
if (resolveIndices) { if (resolveIndices) {
int index = ev.const_value_index; var entry = ev.constant();
switch (ev.tag) { switch (ev.tag()) {
case 'B': case 'B':
print("(byte) "); print("(byte) ");
print(constantWriter.stringValue(index)); print(constantWriter.stringValue(entry));
break; break;
case 'C': case 'C':
print("'"); print("'");
print(constantWriter.charValue(index)); print(constantWriter.charValue(entry));
print("'"); print("'");
break; break;
case 'D': case 'D':
case 'F': case 'F':
case 'I': case 'I':
case 'J': case 'J':
print(constantWriter.stringValue(index)); print(constantWriter.stringValue(entry));
break; break;
case 'S': case 'S':
print("(short) "); print("(short) ");
print(constantWriter.stringValue(index)); print(constantWriter.stringValue(entry));
break; break;
case 'Z': case 'Z':
print(constantWriter.booleanValue(index)); print(constantWriter.booleanValue(entry));
break; break;
case 's': case 's':
print("\""); print("\"");
print(constantWriter.stringValue(index)); print(constantWriter.stringValue(entry));
print("\""); print("\"");
break; break;
default: default:
print(((char) ev.tag) + "#" + ev.const_value_index); print(ev.tag() + "#" + entry.index());
break; break;
} }
} else { } else {
print(((char) ev.tag) + "#" + ev.const_value_index); print(ev.tag() + "#" + ev.constant().index());
} }
return null;
} }
case AnnotationValue.OfEnum ev -> {
@Override
public Void visitEnum(Enum_element_value ev, Boolean resolveIndices) {
if (resolveIndices) { if (resolveIndices) {
writeIndex(ev.type_name_index, resolveIndices); writeIndex(ev.className(), resolveIndices);
print("."); print(".");
writeIndex(ev.const_name_index, resolveIndices); writeIndex(ev.constantName(), resolveIndices);
} else { } else {
print(((char) ev.tag) + "#" + ev.type_name_index + ".#" + ev.const_name_index); print(ev.tag() + "#" + ev.className().index() + ".#"
+ ev.constantName().index());
} }
return null;
} }
case AnnotationValue.OfClass ev -> {
@Override
public Void visitClass(Class_element_value ev, Boolean resolveIndices) {
if (resolveIndices) { if (resolveIndices) {
print("class "); print("class ");
writeIndex(ev.class_info_index, resolveIndices); writeIndex(ev.className(), resolveIndices);
} else { } else {
print(((char) ev.tag) + "#" + ev.class_info_index); print(ev.tag() + "#" + ev.className().index());
} }
return null;
} }
case AnnotationValue.OfAnnotation ev -> {
@Override print(ev.tag());
public Void visitAnnotation(Annotation_element_value ev, Boolean resolveIndices) { AnnotationWriter.this.write(ev.annotation(), resolveIndices);
print((char) ev.tag);
AnnotationWriter.this.write(ev.annotation_value, resolveIndices);
return null;
} }
case AnnotationValue.OfArray ev -> {
@Override
public Void visitArray(Array_element_value ev, Boolean resolveIndices) {
print("["); print("[");
for (int i = 0; i < ev.num_values; i++) { for (int i = 0; i < ev.values().size(); i++) {
if (i > 0) if (i > 0)
print(","); print(",");
write(ev.values[i], resolveIndices); write(ev.values().get(i), resolveIndices);
} }
print("]"); print("]");
return null;
} }
}
} }
private final ClassWriter classWriter; private final ClassWriter classWriter;

View file

@ -26,10 +26,7 @@
package com.sun.tools.javap; package com.sun.tools.javap;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.function.Supplier;
import com.sun.tools.classfile.AttributeException;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.DescriptorException;
/* /*
* A writer similar to a PrintWriter but which does not hide exceptions. * A writer similar to a PrintWriter but which does not hide exceptions.
@ -57,6 +54,14 @@ public class BasicWriter {
lineWriter.print(o == null ? null : o.toString()); lineWriter.print(o == null ? null : o.toString());
} }
protected void print(Supplier<Object> safeguardedCode) {
try {
print(safeguardedCode.get());
} catch (IllegalArgumentException e) {
print(report(e));
}
}
protected void println() { protected void println() {
lineWriter.println(); lineWriter.println();
} }
@ -71,6 +76,11 @@ public class BasicWriter {
lineWriter.println(); lineWriter.println();
} }
protected void println(Supplier<Object> safeguardedCode) {
print(safeguardedCode);
lineWriter.println();
}
protected void indent(int delta) { protected void indent(int delta) {
lineWriter.indent(delta); lineWriter.indent(delta);
} }
@ -83,23 +93,15 @@ public class BasicWriter {
lineWriter.pendingNewline = b; lineWriter.pendingNewline = b;
} }
protected String report(AttributeException e) { protected String report(Exception e) {
out.println("Error: " + e.getMessage()); // i18n?
return "???";
}
protected String report(ConstantPoolException e) {
out.println("Error: " + e.getMessage()); // i18n?
return "???";
}
protected String report(DescriptorException e) {
out.println("Error: " + e.getMessage()); // i18n? out.println("Error: " + e.getMessage()); // i18n?
errorReported = true;
return "???"; return "???";
} }
protected String report(String msg) { protected String report(String msg) {
out.println("Error: " + msg); // i18n? out.println("Error: " + msg); // i18n?
errorReported = true;
return "???"; return "???";
} }
@ -123,6 +125,7 @@ public class BasicWriter {
private LineWriter lineWriter; private LineWriter lineWriter;
private PrintWriter out; private PrintWriter out;
protected Messages messages; protected Messages messages;
protected boolean errorReported;
private static class LineWriter { private static class LineWriter {
static LineWriter instance(Context context) { static LineWriter instance(Context context) {

View file

@ -28,14 +28,15 @@ package com.sun.tools.javap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.sun.tools.classfile.AccessFlags; import java.util.Locale;
import com.sun.tools.classfile.Code_attribute; import java.util.stream.Collectors;
import com.sun.tools.classfile.ConstantPool; import jdk.internal.classfile.Classfile;
import com.sun.tools.classfile.ConstantPoolException; import jdk.internal.classfile.Opcode;
import com.sun.tools.classfile.DescriptorException; import jdk.internal.classfile.constantpool.*;
import com.sun.tools.classfile.Instruction; import jdk.internal.classfile.Instruction;
import com.sun.tools.classfile.Instruction.TypeKind; import jdk.internal.classfile.MethodModel;
import com.sun.tools.classfile.Method; import jdk.internal.classfile.attribute.CodeAttribute;
import jdk.internal.classfile.instruction.*;
/* /*
* Write the contents of a Code attribute. * Write the contents of a Code attribute.
@ -68,161 +69,148 @@ public class CodeWriter extends BasicWriter {
options = Options.instance(context); options = Options.instance(context);
} }
void write(Code_attribute attr, ConstantPool constant_pool) { void write(CodeAttribute attr) {
println("Code:"); println("Code:");
indent(+1); indent(+1);
writeVerboseHeader(attr, constant_pool); writeVerboseHeader(attr);
writeInstrs(attr); writeInstrs(attr);
writeExceptionTable(attr); writeExceptionTable(attr);
attrWriter.write(attr, attr.attributes, constant_pool); attrWriter.write(attr.attributes(), attr);
indent(-1); indent(-1);
} }
public void writeVerboseHeader(Code_attribute attr, ConstantPool constant_pool) { public void writeVerboseHeader(CodeAttribute attr) {
Method method = classWriter.getMethod(); MethodModel method = attr.parent().get();
String argCount; int n = method.methodTypeSymbol().parameterCount();
try { if ((method.flags().flagsMask() & Classfile.ACC_STATIC) == 0)
int n = method.descriptor.getParameterCount(constant_pool);
if (!method.access_flags.is(AccessFlags.ACC_STATIC))
++n; // for 'this' ++n; // for 'this'
argCount = Integer.toString(n); println("stack=" + attr.maxStack() +
} catch (ConstantPoolException e) { ", locals=" + attr.maxLocals() +
argCount = report(e); ", args_size=" + Integer.toString(n));
} catch (DescriptorException e) {
argCount = report(e);
} }
println("stack=" + attr.max_stack + public void writeInstrs(CodeAttribute attr) {
", locals=" + attr.max_locals +
", args_size=" + argCount);
}
public void writeInstrs(Code_attribute attr) {
List<InstructionDetailWriter> detailWriters = getDetailWriters(attr); List<InstructionDetailWriter> detailWriters = getDetailWriters(attr);
for (Instruction instr: attr.getInstructions()) { int pc = 0;
try { try {
for (var coe: attr) {
if (coe instanceof Instruction instr) {
for (InstructionDetailWriter w: detailWriters) for (InstructionDetailWriter w: detailWriters)
w.writeDetails(instr); w.writeDetails(pc, instr);
writeInstr(instr); writeInstr(pc, instr, attr);
} catch (ArrayIndexOutOfBoundsException | IllegalStateException e) { pc += instr.sizeInBytes();
println(report("error at or after byte " + instr.getPC()));
break;
} }
} }
} catch (IllegalArgumentException e) {
report("error at or after byte " + pc);
}
for (InstructionDetailWriter w: detailWriters) for (InstructionDetailWriter w: detailWriters)
w.flush(); w.flush(pc);
} }
public void writeInstr(Instruction instr) { public void writeInstr(int pc, Instruction ins, CodeAttribute lr) {
print(String.format("%4d: %-13s ", instr.getPC(), instr.getMnemonic())); print(String.format("%4d: %-13s ", pc, ins.opcode().name().toLowerCase(Locale.US)));
try {
// compute the number of indentations for the body of multi-line instructions // compute the number of indentations for the body of multi-line instructions
// This is 6 (the width of "%4d: "), divided by the width of each indentation level, // This is 6 (the width of "%4d: "), divided by the width of each indentation level,
// and rounded up to the next integer. // and rounded up to the next integer.
int indentWidth = options.indentWidth; int indentWidth = options.indentWidth;
int indent = (6 + indentWidth - 1) / indentWidth; int indent = (6 + indentWidth - 1) / indentWidth;
instr.accept(instructionPrinter, indent); switch (ins) {
case BranchInstruction instr ->
print(lr.labelToBci(instr.target()));
case ConstantInstruction.ArgumentConstantInstruction instr ->
print(instr.constantValue());
case ConstantInstruction.LoadConstantInstruction instr ->
printConstantPoolRef(instr.constantEntry());
case FieldInstruction instr ->
printConstantPoolRef(instr.field());
case InvokeDynamicInstruction instr ->
printConstantPoolRefAndValue(instr.invokedynamic(), 0);
case InvokeInstruction instr -> {
if (instr.isInterface() && instr.opcode() != Opcode.INVOKESTATIC)
printConstantPoolRefAndValue(instr.method(), instr.count());
else printConstantPoolRef(instr.method());
}
case LoadInstruction instr ->
print(instr.sizeInBytes() > 1 ? instr.slot() : "");
case StoreInstruction instr ->
print(instr.sizeInBytes() > 1 ? instr.slot() : "");
case IncrementInstruction instr ->
print(instr.slot() + ", " + instr.constant());
case LookupSwitchInstruction instr -> {
var cases = instr.cases();
print("{ // " + cases.size());
indent(indent);
for (var c : cases)
print(String.format("%n%12d: %d", c.caseValue(),
lr.labelToBci(c.target())));
print("\n default: " + lr.labelToBci(instr.defaultTarget()) + "\n}");
indent(-indent);
}
case NewMultiArrayInstruction instr ->
printConstantPoolRefAndValue(instr.arrayType(), instr.dimensions());
case NewObjectInstruction instr ->
printConstantPoolRef(instr.className());
case NewPrimitiveArrayInstruction instr ->
print(" " + instr.typeKind().typeName());
case NewReferenceArrayInstruction instr ->
printConstantPoolRef(instr.componentType());
case TableSwitchInstruction instr -> {
print("{ // " + instr.lowValue() + " to " + instr.highValue());
indent(indent);
var caseMap = instr.cases().stream().collect(
Collectors.toMap(SwitchCase::caseValue, SwitchCase::target));
for (int i = instr.lowValue(); i <= instr.highValue(); i++)
print(String.format("%n%12d: %d", i,
lr.labelToBci(caseMap.getOrDefault(i, instr.defaultTarget()))));
print("\n default: " + lr.labelToBci(instr.defaultTarget()) + "\n}");
indent(-indent);
}
case TypeCheckInstruction instr ->
printConstantPoolRef(instr.type());
default -> {}
}
println(); println();
} catch (IllegalArgumentException e) {
println(report(e));
} }
// where
Instruction.KindVisitor<Void,Integer> instructionPrinter =
new Instruction.KindVisitor<>() {
public Void visitNoOperands(Instruction instr, Integer indent) {
return null;
} }
public Void visitArrayType(Instruction instr, TypeKind kind, Integer indent) { private void printConstantPoolRef(PoolEntry entry) {
print(" " + kind.name); print("#" + entry.index());
return null;
}
public Void visitBranch(Instruction instr, int offset, Integer indent) {
print((instr.getPC() + offset));
return null;
}
public Void visitConstantPoolRef(Instruction instr, int index, Integer indent) {
print("#" + index);
tab(); tab();
print("// "); print("// ");
printConstant(index); constantWriter.write(entry.index());
return null;
} }
public Void visitConstantPoolRefAndValue(Instruction instr, int index, int value, Integer indent) { private void printConstantPoolRefAndValue(PoolEntry entry, int value) {
print("#" + index + ", " + value); print("#" + entry.index() + ", " + value);
tab(); tab();
print("// "); print("// ");
printConstant(index); constantWriter.write(entry.index());
return null;
} }
public Void visitLocal(Instruction instr, int index, Integer indent) { public void writeExceptionTable(CodeAttribute attr) {
print(index); var excTable = attr.exceptionHandlers();
return null; if (excTable.size() > 0) {
}
public Void visitLocalAndValue(Instruction instr, int index, int value, Integer indent) {
print(index + ", " + value);
return null;
}
public Void visitLookupSwitch(Instruction instr,
int default_, int npairs, int[] matches, int[] offsets, Integer indent) {
int pc = instr.getPC();
print("{ // " + npairs);
indent(indent);
for (int i = 0; i < npairs; i++) {
print(String.format("%n%12d: %d", matches[i], (pc + offsets[i])));
}
print("\n default: " + (pc + default_) + "\n}");
indent(-indent);
return null;
}
public Void visitTableSwitch(Instruction instr,
int default_, int low, int high, int[] offsets, Integer indent) {
int pc = instr.getPC();
print("{ // " + low + " to " + high);
indent(indent);
for (int i = 0; i < offsets.length; i++) {
print(String.format("%n%12d: %d", (low + i), (pc + offsets[i])));
}
print("\n default: " + (pc + default_) + "\n}");
indent(-indent);
return null;
}
public Void visitValue(Instruction instr, int value, Integer indent) {
print(value);
return null;
}
public Void visitUnknown(Instruction instr, Integer indent) {
return null;
}
};
public void writeExceptionTable(Code_attribute attr) {
if (attr.exception_table_length > 0) {
println("Exception table:"); println("Exception table:");
indent(+1); indent(+1);
println(" from to target type"); println(" from to target type");
for (int i = 0; i < attr.exception_table.length; i++) { for (var handler : excTable) {
Code_attribute.Exception_data handler = attr.exception_table[i];
print(String.format(" %5d %5d %5d", print(String.format(" %5d %5d %5d",
handler.start_pc, handler.end_pc, handler.handler_pc)); attr.labelToBci(handler.tryStart()),
attr.labelToBci(handler.tryEnd()),
attr.labelToBci(handler.handler())));
print(" "); print(" ");
int catch_type = handler.catch_type; var catch_type = handler.catchType();
if (catch_type == 0) { if (catch_type.isEmpty()) {
println("any"); println("any");
} else { } else {
print("Class "); print("Class ");
println(constantWriter.stringValue(catch_type)); println(constantWriter.stringValue(catch_type.get()));
} }
} }
indent(-1); indent(-1);
@ -230,14 +218,10 @@ public class CodeWriter extends BasicWriter {
} }
private void printConstant(int index) { private List<InstructionDetailWriter> getDetailWriters(CodeAttribute attr) {
constantWriter.write(index);
}
private List<InstructionDetailWriter> getDetailWriters(Code_attribute attr) {
List<InstructionDetailWriter> detailWriters = new ArrayList<>(); List<InstructionDetailWriter> detailWriters = new ArrayList<>();
if (options.details.contains(InstructionDetailWriter.Kind.SOURCE)) { if (options.details.contains(InstructionDetailWriter.Kind.SOURCE)) {
sourceWriter.reset(classWriter.getClassFile(), attr); sourceWriter.reset(attr);
if (sourceWriter.hasSource()) if (sourceWriter.hasSource())
detailWriters.add(sourceWriter); detailWriters.add(sourceWriter);
else else

View file

@ -25,11 +25,8 @@
package com.sun.tools.javap; package com.sun.tools.javap;
import com.sun.tools.classfile.ClassFile; import jdk.internal.classfile.constantpool.*;
import com.sun.tools.classfile.ConstantPool; import static jdk.internal.classfile.Classfile.*;
import com.sun.tools.classfile.ConstantPoolException;
import static com.sun.tools.classfile.ConstantPool.*;
/* /*
* Write a constant pool entry. * Write a constant pool entry.
@ -55,122 +52,11 @@ public class ConstantWriter extends BasicWriter {
} }
protected void writeConstantPool() { protected void writeConstantPool() {
ConstantPool constant_pool = classWriter.getClassFile().constant_pool; var constant_pool = classWriter.getClassModel().constantPool();
writeConstantPool(constant_pool); writeConstantPool(constant_pool);
} }
protected void writeConstantPool(ConstantPool constant_pool) { protected void writeConstantPool(ConstantPool constant_pool) {
ConstantPool.Visitor<Integer, Void> v = new ConstantPool.Visitor<>() {
public Integer visitClass(CONSTANT_Class_info info, Void p) {
print("#" + info.name_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitDouble(CONSTANT_Double_info info, Void p) {
println(stringValue(info));
return 2;
}
public Integer visitFieldref(CONSTANT_Fieldref_info info, Void p) {
print("#" + info.class_index + ".#" + info.name_and_type_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitFloat(CONSTANT_Float_info info, Void p) {
println(stringValue(info));
return 1;
}
public Integer visitInteger(CONSTANT_Integer_info info, Void p) {
println(stringValue(info));
return 1;
}
public Integer visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) {
print("#" + info.class_index + ".#" + info.name_and_type_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, Void p) {
print("#" + info.bootstrap_method_attr_index + ":#" + info.name_and_type_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitDynamicConstant(CONSTANT_Dynamic_info info, Void p) {
print("#" + info.bootstrap_method_attr_index + ":#" + info.name_and_type_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitLong(CONSTANT_Long_info info, Void p) {
println(stringValue(info));
return 2;
}
public Integer visitMethodref(CONSTANT_Methodref_info info, Void p) {
print("#" + info.class_index + ".#" + info.name_and_type_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitMethodHandle(CONSTANT_MethodHandle_info info, Void p) {
print(info.reference_kind.tag + ":#" + info.reference_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitMethodType(CONSTANT_MethodType_info info, Void p) {
print("#" + info.descriptor_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitModule(CONSTANT_Module_info info, Void p) {
print("#" + info.name_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitNameAndType(CONSTANT_NameAndType_info info, Void p) {
print("#" + info.name_index + ":#" + info.type_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitPackage(CONSTANT_Package_info info, Void p) {
print("#" + info.name_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitString(CONSTANT_String_info info, Void p) {
print("#" + info.string_index);
tab();
println("// " + stringValue(info));
return 1;
}
public Integer visitUtf8(CONSTANT_Utf8_info info, Void p) {
println(stringValue(info));
return 1;
}
};
println("Constant pool:"); println("Constant pool:");
indent(+1); indent(+1);
int width = String.valueOf(constant_pool.size()).length() + 1; int width = String.valueOf(constant_pool.size()).length() + 1;
@ -178,321 +64,228 @@ public class ConstantWriter extends BasicWriter {
while (cpx < constant_pool.size()) { while (cpx < constant_pool.size()) {
print(String.format("%" + width + "s", ("#" + cpx))); print(String.format("%" + width + "s", ("#" + cpx)));
try { try {
CPInfo cpInfo = constant_pool.get(cpx); var cpInfo = constant_pool.entryByIndex(cpx);
print(String.format(" = %-18s ", cpTagName(cpInfo))); print(String.format(" = %-18s ", cpTagName(cpInfo.tag())));
cpx += cpInfo.accept(v, null); switch (cpInfo) {
} catch (ConstantPool.InvalidIndex ex) { case ClassEntry info -> {
// should not happen 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); indent(-1);
} }
protected void write(int cpx) { protected void write(int cpx) {
ClassFile classFile = classWriter.getClassFile();
if (cpx == 0) { if (cpx == 0) {
print("#0"); print("#0");
return; return;
} }
var classModel = classWriter.getClassModel();
CPInfo cpInfo; var cpInfo = classModel.constantPool().entryByIndex(cpx);
try { var tag = cpInfo.tag();
cpInfo = classFile.constant_pool.get(cpx); if (cpInfo instanceof MemberRefEntry ref) {
} catch (ConstantPoolException e) {
print("#" + cpx);
return;
}
int tag = cpInfo.getTag();
switch (tag) {
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
case CONSTANT_Fieldref:
// simplify references within this class // simplify references within this class
CPRefInfo ref = (CPRefInfo) cpInfo; if (ref.owner().index() == classModel.thisClass().index())
try { cpInfo = ref.nameAndType();
if (ref.class_index == classFile.this_class)
cpInfo = classFile.constant_pool.get(ref.name_and_type_index);
} catch (ConstantPool.InvalidIndex e) {
// ignore, for now
}
} }
print(tagName(tag) + " " + stringValue(cpInfo)); print(tagName(tag) + " " + stringValue(cpInfo));
} }
String cpTagName(CPInfo cpInfo) { String cpTagName(int tag) {
String n = cpInfo.getClass().getSimpleName(); return switch (tag) {
return n.replace("CONSTANT_", "").replace("_info", ""); 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_METHODHANDLE -> "MethodHandle";
case TAG_METHODTYPE -> "MethodType";
case TAG_METHODREF -> "Methodref";
case TAG_INTERFACEMETHODREF -> "InterfaceMethodref";
case TAG_INVOKEDYNAMIC -> "InvokeDynamic";
case TAG_CONSTANTDYNAMIC -> "Dynamic";
case TAG_NAMEANDTYPE -> "NameAndType";
default -> "Unknown";
};
} }
String tagName(int tag) { String tagName(int tag) {
switch (tag) { return switch (tag) {
case CONSTANT_Utf8: case TAG_UTF8 -> "Utf8";
return "Utf8"; case TAG_INTEGER -> "int";
case CONSTANT_Integer: case TAG_FLOAT -> "float";
return "int"; case TAG_LONG -> "long";
case CONSTANT_Float: case TAG_DOUBLE -> "double";
return "float"; case TAG_CLASS -> "class";
case CONSTANT_Long: case TAG_STRING -> "String";
return "long"; case TAG_FIELDREF -> "Field";
case CONSTANT_Double: case TAG_METHODHANDLE -> "MethodHandle";
return "double"; case TAG_METHODTYPE -> "MethodType";
case CONSTANT_Class: case TAG_METHODREF -> "Method";
return "class"; case TAG_INTERFACEMETHODREF -> "InterfaceMethod";
case CONSTANT_String: case TAG_INVOKEDYNAMIC -> "InvokeDynamic";
return "String"; case TAG_CONSTANTDYNAMIC -> "Dynamic";
case CONSTANT_Fieldref: case TAG_NAMEANDTYPE -> "NameAndType";
return "Field"; default -> "(unknown tag " + tag + ")";
case CONSTANT_MethodHandle: };
return "MethodHandle";
case CONSTANT_MethodType:
return "MethodType";
case CONSTANT_Methodref:
return "Method";
case CONSTANT_InterfaceMethodref:
return "InterfaceMethod";
case CONSTANT_InvokeDynamic:
return "InvokeDynamic";
case CONSTANT_Dynamic:
return "Dynamic";
case CONSTANT_NameAndType:
return "NameAndType";
default:
return "(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) { String booleanValue(int constant_pool_index) {
ClassFile classFile = classWriter.getClassFile(); var info = classWriter.getClassModel().constantPool()
try { .entryByIndex(constant_pool_index);
CPInfo info = classFile.constant_pool.get(constant_pool_index); if (info instanceof IntegerEntry ie) {
if (info instanceof CONSTANT_Integer_info) { switch (ie.intValue()) {
int value = ((CONSTANT_Integer_info) info).value;
switch (value) {
case 0: return "false"; case 0: return "false";
case 1: return "true"; case 1: return "true";
} }
} }
return "#" + constant_pool_index; return "#" + constant_pool_index;
} catch (ConstantPool.InvalidIndex e) { }
return report(e);
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) { String charValue(int constant_pool_index) {
ClassFile classFile = classWriter.getClassFile(); var info = classWriter.getClassModel().constantPool()
try { .entryByIndex(constant_pool_index);
CPInfo info = classFile.constant_pool.get(constant_pool_index); if (info instanceof IntegerEntry ie) {
if (info instanceof CONSTANT_Integer_info) { int value = ie.intValue();
int value = ((CONSTANT_Integer_info) info).value;
return String.valueOf((char) value); return String.valueOf((char) value);
} else { } else {
return "#" + constant_pool_index; return "#" + constant_pool_index;
} }
} catch (ConstantPool.InvalidIndex e) {
return report(e);
}
} }
String stringValue(int constant_pool_index) { String stringValue(int constant_pool_index) {
ClassFile classFile = classWriter.getClassFile(); return stringValue(classWriter.getClassModel().constantPool()
try { .entryByIndex(constant_pool_index));
return stringValue(classFile.constant_pool.get(constant_pool_index));
} catch (ConstantPool.InvalidIndex e) {
return report(e);
}
} }
String stringValue(CPInfo cpInfo) { String stringValue(PoolEntry cpInfo) {
return stringValueVisitor.visit(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();
StringValueVisitor stringValueVisitor = new StringValueVisitor(); case StringEntry info -> stringValue(info.utf8());
case Utf8Entry info -> {
private class StringValueVisitor implements ConstantPool.Visitor<String, Void> {
public String visit(CPInfo info) {
return info.accept(this, null);
}
public String visitClass(CONSTANT_Class_info info, Void p) {
return getCheckedName(info);
}
String getCheckedName(CONSTANT_Class_info info) {
try {
return checkName(info.getName());
} catch (ConstantPoolException e) {
return report(e);
}
}
public String visitDouble(CONSTANT_Double_info info, Void p) {
return info.value + "d";
}
public String visitFieldref(CONSTANT_Fieldref_info info, Void p) {
return visitRef(info, p);
}
public String visitFloat(CONSTANT_Float_info info, Void p) {
return info.value + "f";
}
public String visitInteger(CONSTANT_Integer_info info, Void p) {
return String.valueOf(info.value);
}
public String visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) {
return visitRef(info, p);
}
public String visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, Void p) {
try {
String callee = stringValue(info.getNameAndTypeInfo());
return "#" + info.bootstrap_method_attr_index + ":" + callee;
} catch (ConstantPoolException e) {
return report(e);
}
}
public String visitDynamicConstant(CONSTANT_Dynamic_info info, Void p) {
try {
String callee = stringValue(info.getNameAndTypeInfo());
return "#" + info.bootstrap_method_attr_index + ":" + callee;
} catch (ConstantPoolException e) {
return report(e);
}
}
public String visitLong(CONSTANT_Long_info info, Void p) {
return info.value + "l";
}
public String visitModule(CONSTANT_Module_info info, Void p) {
try {
return checkName(info.getName());
} catch (ConstantPoolException e) {
return report(e);
}
}
public String visitNameAndType(CONSTANT_NameAndType_info info, Void p) {
return getCheckedName(info) + ":" + getType(info);
}
String getCheckedName(CONSTANT_NameAndType_info info) {
try {
return checkName(info.getName());
} catch (ConstantPoolException e) {
return report(e);
}
}
public String visitPackage(CONSTANT_Package_info info, Void p) {
try {
return checkName(info.getName());
} catch (ConstantPoolException e) {
return report(e);
}
}
String getType(CONSTANT_NameAndType_info info) {
try {
return info.getType();
} catch (ConstantPoolException e) {
return report(e);
}
}
public String visitMethodHandle(CONSTANT_MethodHandle_info info, Void p) {
try {
return info.reference_kind + " " + stringValue(info.getCPRefInfo());
} catch (ConstantPoolException e) {
return report(e);
}
}
public String visitMethodType(CONSTANT_MethodType_info info, Void p) {
try {
return info.getType();
} catch (ConstantPoolException e) {
return report(e);
}
}
public String visitMethodref(CONSTANT_Methodref_info info, Void p) {
return visitRef(info, p);
}
public String visitString(CONSTANT_String_info info, Void p) {
try {
ClassFile classFile = classWriter.getClassFile();
int string_index = info.string_index;
return stringValue(classFile.constant_pool.getUTF8Info(string_index));
} catch (ConstantPoolException e) {
return report(e);
}
}
public String visitUtf8(CONSTANT_Utf8_info info, Void p) {
String s = info.value;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) { for (char c : info.stringValue().toCharArray()) {
char c = s.charAt(i); sb.append(switch (c) {
switch (c) { case '\t' -> "\\t";
case '\t': case '\n' -> "\\n";
sb.append('\\').append('t'); case '\r' -> "\\r";
break; case '\b' -> "\\b";
case '\n': case '\f' -> "\\f";
sb.append('\\').append('n'); case '\"' -> "\\\"";
break; case '\'' -> "\\\'";
case '\r': case '\\' -> "\\\\";
sb.append('\\').append('r'); default -> Character.isISOControl(c)
break; ? String.format("\\u%04x", (int) c) : c;
case '\b': });
sb.append('\\').append('b');
break;
case '\f':
sb.append('\\').append('f');
break;
case '\"':
sb.append('\\').append('\"');
break;
case '\'':
sb.append('\\').append('\'');
break;
case '\\':
sb.append('\\').append('\\');
break;
default:
if (Character.isISOControl(c)) {
sb.append(String.format("\\u%04x", (int) c));
break;
}
sb.append(c);
}
}
return sb.toString();
}
String visitRef(CPRefInfo info, Void p) {
String cn = getCheckedClassName(info);
String nat;
try {
nat = stringValue(info.getNameAndTypeInfo());
} catch (ConstantPoolException e) {
nat = report(e);
}
return cn + "." + nat;
}
String getCheckedClassName(CPRefInfo info) {
try {
return checkName(info.getClassName());
} catch (ConstantPoolException e) {
return report(e);
} }
yield sb.toString();
} }
default -> throw new IllegalArgumentException("unknown " + cpInfo);
};
} }
/* If name is a valid binary name, return it; otherwise quote it. */ /* If name is a valid binary name, return it; otherwise quote it. */

View file

@ -25,7 +25,7 @@
package com.sun.tools.javap; package com.sun.tools.javap;
import com.sun.tools.classfile.Instruction; import jdk.internal.classfile.Instruction;
/* /*
@ -56,6 +56,6 @@ public abstract class InstructionDetailWriter extends BasicWriter {
super(context); super(context);
} }
abstract void writeDetails(Instruction instr); abstract void writeDetails(int pc, Instruction instr);
void flush() { } void flush(int pc) { }
} }

View file

@ -67,7 +67,10 @@ import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager; import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation; import javax.tools.StandardLocation;
import com.sun.tools.classfile.*; import jdk.internal.classfile.ClassModel;
import jdk.internal.classfile.Classfile;
import jdk.internal.classfile.constantpool.*;
import static jdk.internal.classfile.Classfile.*;
/** /**
* "Main" class for javap, normally accessed from the command line * "Main" class for javap, normally accessed from the command line
@ -166,7 +169,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
@Override @Override
void process(JavapTask task, String opt, String arg) { void process(JavapTask task, String opt, String arg) {
task.options.accessOptions.add(opt); task.options.accessOptions.add(opt);
task.options.showAccess = AccessFlags.ACC_PUBLIC; task.options.showAccess = ACC_PUBLIC;
} }
}, },
@ -174,7 +177,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
@Override @Override
void process(JavapTask task, String opt, String arg) { void process(JavapTask task, String opt, String arg) {
task.options.accessOptions.add(opt); task.options.accessOptions.add(opt);
task.options.showAccess = AccessFlags.ACC_PROTECTED; task.options.showAccess = ACC_PROTECTED;
} }
}, },
@ -193,7 +196,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
!task.options.accessOptions.contains("-private")) { !task.options.accessOptions.contains("-private")) {
task.options.accessOptions.add(opt); task.options.accessOptions.add(opt);
} }
task.options.showAccess = AccessFlags.ACC_PRIVATE; task.options.showAccess = ACC_PRIVATE;
} }
}, },
@ -349,7 +352,6 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
context = new Context(); context = new Context();
context.put(Messages.class, this); context.put(Messages.class, this);
options = Options.instance(context); options = Options.instance(context);
attributeFactory = new Attribute.Factory();
} }
public JavapTask(Writer out, public JavapTask(Writer out,
@ -633,7 +635,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
reportError("err.nomem"); reportError("err.nomem");
result = EXIT_ERROR; result = EXIT_ERROR;
} catch (FatalError e) { } catch (IllegalArgumentException e) {
Object msg = e.getLocalizedMessage(); Object msg = e.getLocalizedMessage();
if (msg == null) { if (msg == null) {
msg = e; msg = e;
@ -654,7 +656,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
} }
protected int writeClass(ClassWriter classWriter, String className) protected int writeClass(ClassWriter classWriter, String className)
throws IOException, ConstantPoolException { throws IOException {
JavaFileObject fo = open(className); JavaFileObject fo = open(className);
if (fo == null) { if (fo == null) {
reportError("err.class.not.found", className); reportError("err.class.not.found", className);
@ -663,34 +665,26 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
ClassFileInfo cfInfo = read(fo); ClassFileInfo cfInfo = read(fo);
if (!className.endsWith(".class")) { if (!className.endsWith(".class")) {
if (cfInfo.cf.this_class == 0) { String cfName = cfInfo.cm.thisClass().asInternalName();
if (!className.equals("module-info")) {
reportWarning("warn.unexpected.class", fo.getName(), className);
}
} else {
String cfName = cfInfo.cf.getName();
if (!cfName.replaceAll("[/$]", ".").equals(className.replaceAll("[/$]", "."))) { if (!cfName.replaceAll("[/$]", ".").equals(className.replaceAll("[/$]", "."))) {
reportWarning("warn.unexpected.class", fo.getName(), className); reportWarning("warn.unexpected.class", fo.getName(), className);
} }
} }
} if (!write(cfInfo)) return EXIT_ERROR;
write(cfInfo);
if (options.showInnerClasses) { if (options.showInnerClasses) {
ClassFile cf = cfInfo.cf; ClassModel cm = cfInfo.cm;
Attribute a = cf.getAttribute(Attribute.InnerClasses); var a = cm.findAttribute(jdk.internal.classfile.Attributes.INNER_CLASSES);
if (a instanceof InnerClasses_attribute) { if (a.isPresent()) {
InnerClasses_attribute inners = (InnerClasses_attribute) a; var inners = a.get();
try { try {
int result = EXIT_OK; int result = EXIT_OK;
for (int i = 0; i < inners.classes.length; i++) { for (var inner : inners.classes()) {
int outerIndex = inners.classes[i].outer_class_info_index; var outerClassInfo = inner.outerClass();
ConstantPool.CONSTANT_Class_info outerClassInfo = cf.constant_pool.getClassInfo(outerIndex); String outerClassName = outerClassInfo.map(ClassEntry::asInternalName).orElse(null);
String outerClassName = outerClassInfo.getName(); if (cm.thisClass().asInternalName().equals(outerClassName)) {
if (outerClassName.equals(cf.getName())) { var innerClassInfo = inner.innerClass();
int innerIndex = inners.classes[i].inner_class_info_index; String innerClassName = innerClassInfo.asInternalName();
ConstantPool.CONSTANT_Class_info innerClassInfo = cf.constant_pool.getClassInfo(innerIndex);
String innerClassName = innerClassInfo.getName();
classWriter.println("// inner class " + innerClassName.replaceAll("[/$]", ".")); classWriter.println("// inner class " + innerClassName.replaceAll("[/$]", "."));
classWriter.println(); classWriter.println();
result = writeClass(classWriter, innerClassName); result = writeClass(classWriter, innerClassName);
@ -698,13 +692,10 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
} }
} }
return result; return result;
} catch (ConstantPoolException e) { } catch (IllegalArgumentException e) {
reportError("err.bad.innerclasses.attribute", className); reportError("err.bad.innerclasses.attribute", className);
return EXIT_ERROR; return EXIT_ERROR;
} }
} else if (a != null) {
reportError("err.bad.innerclasses.attribute", className);
return EXIT_ERROR;
} }
} }
@ -811,19 +802,19 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
} }
public static class ClassFileInfo { public static class ClassFileInfo {
ClassFileInfo(JavaFileObject fo, ClassFile cf, byte[] digest, int size) { ClassFileInfo(JavaFileObject fo, ClassModel cm, byte[] digest, int size) {
this.fo = fo; this.fo = fo;
this.cf = cf; this.cm = cm;
this.digest = digest; this.digest = digest;
this.size = size; this.size = size;
} }
public final JavaFileObject fo; public final JavaFileObject fo;
public final ClassFile cf; public final ClassModel cm;
public final byte[] digest; public final byte[] digest;
public final int size; public final int size;
} }
public ClassFileInfo read(JavaFileObject fo) throws IOException, ConstantPoolException { public ClassFileInfo read(JavaFileObject fo) throws IOException {
InputStream in = fo.openInputStream(); InputStream in = fo.openInputStream();
try { try {
SizeInputStream sizeIn = null; SizeInputStream sizeIn = null;
@ -836,17 +827,16 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
in = new DigestInputStream(in, md); in = new DigestInputStream(in, md);
in = sizeIn = new SizeInputStream(in); in = sizeIn = new SizeInputStream(in);
} }
ClassModel cm = Classfile.of().parse(in.readAllBytes());
ClassFile cf = ClassFile.read(in, attributeFactory);
byte[] digest = (md == null) ? null : md.digest(); byte[] digest = (md == null) ? null : md.digest();
int size = (sizeIn == null) ? -1 : sizeIn.size(); int size = (sizeIn == null) ? -1 : sizeIn.size();
return new ClassFileInfo(fo, cf, digest, size); return new ClassFileInfo(fo, cm, digest, size);
} finally { } finally {
in.close(); in.close();
} }
} }
public void write(ClassFileInfo info) { public boolean write(ClassFileInfo info) {
ClassWriter classWriter = ClassWriter.instance(context); ClassWriter classWriter = ClassWriter.instance(context);
if (options.sysInfo || options.verbose) { if (options.sysInfo || options.verbose) {
classWriter.setFile(info.fo.toUri()); classWriter.setFile(info.fo.toUri());
@ -855,56 +845,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
classWriter.setFileSize(info.size); classWriter.setFileSize(info.size);
} }
classWriter.write(info.cf); return classWriter.write(info.cm);
}
protected void setClassFile(ClassFile classFile) {
ClassWriter classWriter = ClassWriter.instance(context);
classWriter.setClassFile(classFile);
}
protected void setMethod(Method enclosingMethod) {
ClassWriter classWriter = ClassWriter.instance(context);
classWriter.setMethod(enclosingMethod);
}
protected void write(Attribute value) {
AttributeWriter attrWriter = AttributeWriter.instance(context);
ClassWriter classWriter = ClassWriter.instance(context);
ClassFile cf = classWriter.getClassFile();
attrWriter.write(cf, value, cf.constant_pool);
}
protected void write(Attributes attrs) {
AttributeWriter attrWriter = AttributeWriter.instance(context);
ClassWriter classWriter = ClassWriter.instance(context);
ClassFile cf = classWriter.getClassFile();
attrWriter.write(cf, attrs, cf.constant_pool);
}
protected void write(ConstantPool constant_pool) {
ConstantWriter constantWriter = ConstantWriter.instance(context);
constantWriter.writeConstantPool(constant_pool);
}
protected void write(ConstantPool constant_pool, int value) {
ConstantWriter constantWriter = ConstantWriter.instance(context);
constantWriter.write(value);
}
protected void write(ConstantPool.CPInfo value) {
ConstantWriter constantWriter = ConstantWriter.instance(context);
constantWriter.println(value);
}
protected void write(Field value) {
ClassWriter classWriter = ClassWriter.instance(context);
classWriter.writeField(value);
}
protected void write(Method value) {
ClassWriter classWriter = ClassWriter.instance(context);
classWriter.writeMethod(value);
} }
private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) { private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
@ -944,7 +885,8 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
if (result == null) if (result == null)
result = l; result = l;
else else
throw new IOException("multiple definitions found for " + moduleName); throw new IOException("multiple definitions found for "
+ moduleName);
} }
} }
if (result != null) if (result != null)
@ -957,7 +899,8 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
private void showHelp() { private void showHelp() {
printLines(getMessage("main.usage", progname)); printLines(getMessage("main.usage", progname));
for (Option o: recognizedOptions) { for (Option o: recognizedOptions) {
String name = o.aliases[0].replaceAll("^-+", "").replaceAll("-+", "_"); // there must always be at least one name // there must always be at least one name
String name = o.aliases[0].replaceAll("^-+", "").replaceAll("-+", "_");
if (name.startsWith("X") || name.equals("fullversion")) if (name.startsWith("X") || name.equals("fullversion"))
continue; continue;
printLines(getMessage("main.opt." + name)); printLines(getMessage("main.opt." + name));
@ -1000,7 +943,8 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
try { try {
versionRB = ResourceBundle.getBundle(versionRBName); versionRB = ResourceBundle.getBundle(versionRBName);
} catch (MissingResourceException e) { } catch (MissingResourceException e) {
return getMessage("version.resource.missing", System.getProperty("java.version")); return getMessage("version.resource.missing",
System.getProperty("java.version"));
} }
} }
try { try {
@ -1064,7 +1008,8 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
@Override @Override
public String toString() { public String toString() {
return getClass().getName() + "[key=" + key + ",args=" + Arrays.asList(args) + "]"; return getClass().getName() + "[key=" + key + ",args="
+ Arrays.asList(args) + "]";
} }
}; };
@ -1089,10 +1034,12 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
ResourceBundle b = bundles.get(locale); ResourceBundle b = bundles.get(locale);
if (b == null) { if (b == null) {
try { try {
b = ResourceBundle.getBundle("com.sun.tools.javap.resources.javap", locale); b = ResourceBundle.getBundle("com.sun.tools.javap.resources.javap",
locale);
bundles.put(locale, b); bundles.put(locale, b);
} catch (MissingResourceException e) { } catch (MissingResourceException e) {
throw new InternalError("Cannot find javap resource bundle for locale " + locale); throw new InternalError("Cannot find javap resource bundle for locale "
+ locale);
} }
} }
@ -1114,7 +1061,6 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
//ResourceBundle bundle; //ResourceBundle bundle;
Locale task_locale; Locale task_locale;
Map<Locale, ResourceBundle> bundles; Map<Locale, ResourceBundle> bundles;
protected Attribute.Factory attributeFactory;
private static final String progname = "javap"; private static final String progname = "javap";

View file

@ -25,19 +25,15 @@
package com.sun.tools.javap; package com.sun.tools.javap;
import com.sun.tools.classfile.Attribute;
import com.sun.tools.classfile.Code_attribute;
import com.sun.tools.classfile.ConstantPool;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Descriptor;
import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
import com.sun.tools.classfile.Instruction;
import com.sun.tools.classfile.LocalVariableTable_attribute;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import jdk.internal.classfile.Attributes;
import jdk.internal.classfile.CodeModel;
import jdk.internal.classfile.Instruction;
import jdk.internal.classfile.Signature;
import jdk.internal.classfile.attribute.LocalVariableInfo;
/** /**
* Annotate instructions with details about local variables. * Annotate instructions with details about local variables.
@ -50,19 +46,21 @@ import java.util.Map;
public class LocalVariableTableWriter extends InstructionDetailWriter { public class LocalVariableTableWriter extends InstructionDetailWriter {
public enum NoteKind { public enum NoteKind {
START("start") { START("start") {
public boolean match(LocalVariableTable_attribute.Entry entry, int pc) { @Override
return (pc == entry.start_pc); public boolean match(LocalVariableInfo entry, int pc) {
return (pc == entry.startPc());
} }
}, },
END("end") { END("end") {
public boolean match(LocalVariableTable_attribute.Entry entry, int pc) { @Override
return (pc == entry.start_pc + entry.length); public boolean match(LocalVariableInfo entry, int pc) {
return (pc == entry.startPc() + entry.length());
} }
}; };
NoteKind(String text) { NoteKind(String text) {
this.text = text; this.text = text;
} }
public abstract boolean match(LocalVariableTable_attribute.Entry entry, int pc); public abstract boolean match(LocalVariableInfo entry, int pc);
public final String text; public final String text;
} }
@ -79,71 +77,56 @@ public class LocalVariableTableWriter extends InstructionDetailWriter {
classWriter = ClassWriter.instance(context); classWriter = ClassWriter.instance(context);
} }
public void reset(Code_attribute attr) { public void reset(CodeModel attr) {
codeAttr = attr; codeAttr = attr;
pcMap = new HashMap<>(); pcMap = new HashMap<>();
LocalVariableTable_attribute lvt = var lvt = attr.findAttribute(Attributes.LOCAL_VARIABLE_TABLE);
(LocalVariableTable_attribute) (attr.attributes.get(Attribute.LocalVariableTable));
if (lvt == null) if (lvt.isEmpty())
return; return;
for (int i = 0; i < lvt.local_variable_table.length; i++) { for (var entry : lvt.get().localVariables()) {
LocalVariableTable_attribute.Entry entry = lvt.local_variable_table[i]; put(entry.startPc(), entry);
put(entry.start_pc, entry); put(entry.startPc() + entry.length(), entry);
put(entry.start_pc + entry.length, entry);
} }
} }
public void writeDetails(Instruction instr) { @Override
int pc = instr.getPC(); public void writeDetails(int pc, Instruction instr) {
writeLocalVariables(pc, NoteKind.END); writeLocalVariables(pc, NoteKind.END);
writeLocalVariables(pc, NoteKind.START); writeLocalVariables(pc, NoteKind.START);
} }
@Override @Override
public void flush() { public void flush(int pc) {
int pc = codeAttr.code_length;
writeLocalVariables(pc, NoteKind.END); writeLocalVariables(pc, NoteKind.END);
} }
public void writeLocalVariables(int pc, NoteKind kind) { public void writeLocalVariables(int pc, NoteKind kind) {
ConstantPool constant_pool = classWriter.getClassFile().constant_pool;
String indent = space(2); // get from Options? String indent = space(2); // get from Options?
List<LocalVariableTable_attribute.Entry> entries = pcMap.get(pc); var entries = pcMap.get(pc);
if (entries != null) { if (entries != null) {
for (ListIterator<LocalVariableTable_attribute.Entry> iter = for (var iter = entries.listIterator(kind == NoteKind.END ? entries.size() : 0);
entries.listIterator(kind == NoteKind.END ? entries.size() : 0);
kind == NoteKind.END ? iter.hasPrevious() : iter.hasNext() ; ) { kind == NoteKind.END ? iter.hasPrevious() : iter.hasNext() ; ) {
LocalVariableTable_attribute.Entry entry = var entry = kind == NoteKind.END ? iter.previous() : iter.next();
kind == NoteKind.END ? iter.previous() : iter.next();
if (kind.match(entry, pc)) { if (kind.match(entry, pc)) {
print(indent); print(indent);
print(kind.text); print(kind.text);
print(" local "); print(" local ");
print(entry.index); print(entry.slot());
print(" // "); print(" // ");
Descriptor d = new Descriptor(entry.descriptor_index); print(classWriter.sigPrinter.print(
try { Signature.parseFrom(entry.type().stringValue())));
print(d.getFieldType(constant_pool));
} catch (InvalidDescriptor e) {
print(report(e));
} catch (ConstantPoolException e) {
print(report(e));
}
print(" "); print(" ");
try { print(entry.name().stringValue());
print(constant_pool.getUTF8Value(entry.name_index));
} catch (ConstantPoolException e) {
print(report(e));
}
println(); println();
} }
} }
} }
} }
private void put(int pc, LocalVariableTable_attribute.Entry entry) { private void put(int pc, LocalVariableInfo entry) {
List<LocalVariableTable_attribute.Entry> list = pcMap.get(pc); var list = pcMap.get(pc);
if (list == null) { if (list == null) {
list = new ArrayList<>(); list = new ArrayList<>();
pcMap.put(pc, list); pcMap.put(pc, list);
@ -153,6 +136,6 @@ public class LocalVariableTableWriter extends InstructionDetailWriter {
} }
private ClassWriter classWriter; private ClassWriter classWriter;
private Code_attribute codeAttr; private CodeModel codeAttr;
private Map<Integer, List<LocalVariableTable_attribute.Entry>> pcMap; private Map<Integer, List<LocalVariableInfo>> pcMap;
} }

View file

@ -25,20 +25,15 @@
package com.sun.tools.javap; package com.sun.tools.javap;
import com.sun.tools.classfile.Attribute;
import com.sun.tools.classfile.Code_attribute;
import com.sun.tools.classfile.ConstantPool;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Descriptor;
import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
import com.sun.tools.classfile.Instruction;
import com.sun.tools.classfile.LocalVariableTypeTable_attribute;
import com.sun.tools.classfile.Signature;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import jdk.internal.classfile.Attributes;
import jdk.internal.classfile.CodeModel;
import jdk.internal.classfile.Instruction;
import jdk.internal.classfile.Signature;
import jdk.internal.classfile.attribute.LocalVariableTypeInfo;
/** /**
* Annotate instructions with details about local variables. * Annotate instructions with details about local variables.
@ -51,19 +46,21 @@ import java.util.Map;
public class LocalVariableTypeTableWriter extends InstructionDetailWriter { public class LocalVariableTypeTableWriter extends InstructionDetailWriter {
public enum NoteKind { public enum NoteKind {
START("start") { START("start") {
public boolean match(LocalVariableTypeTable_attribute.Entry entry, int pc) { @Override
return (pc == entry.start_pc); public boolean match(LocalVariableTypeInfo entry, int pc) {
return (pc == entry.startPc());
} }
}, },
END("end") { END("end") {
public boolean match(LocalVariableTypeTable_attribute.Entry entry, int pc) { @Override
return (pc == entry.start_pc + entry.length); public boolean match(LocalVariableTypeInfo entry, int pc) {
return (pc == entry.startPc() + entry.length());
} }
}; };
NoteKind(String text) { NoteKind(String text) {
this.text = text; this.text = text;
} }
public abstract boolean match(LocalVariableTypeTable_attribute.Entry entry, int pc); public abstract boolean match(LocalVariableTypeInfo entry, int pc);
public final String text; public final String text;
} }
@ -80,71 +77,60 @@ public class LocalVariableTypeTableWriter extends InstructionDetailWriter {
classWriter = ClassWriter.instance(context); classWriter = ClassWriter.instance(context);
} }
public void reset(Code_attribute attr) { public void reset(CodeModel attr) {
codeAttr = attr; codeAttr = attr;
pcMap = new HashMap<>(); pcMap = new HashMap<>();
LocalVariableTypeTable_attribute lvt = var lvt = attr.findAttribute(Attributes.LOCAL_VARIABLE_TYPE_TABLE);
(LocalVariableTypeTable_attribute) (attr.attributes.get(Attribute.LocalVariableTypeTable));
if (lvt == null) if (lvt.isEmpty())
return; return;
for (int i = 0; i < lvt.local_variable_table.length; i++) { for (var entry : lvt.get().localVariableTypes()) {
LocalVariableTypeTable_attribute.Entry entry = lvt.local_variable_table[i]; put(entry.startPc(), entry);
put(entry.start_pc, entry); put(entry.startPc() + entry.length(), entry);
put(entry.start_pc + entry.length, entry);
} }
} }
public void writeDetails(Instruction instr) { @Override
int pc = instr.getPC(); public void writeDetails(int pc, Instruction instr) {
writeLocalVariables(pc, NoteKind.END); writeLocalVariables(pc, NoteKind.END);
writeLocalVariables(pc, NoteKind.START); writeLocalVariables(pc, NoteKind.START);
} }
@Override @Override
public void flush() { public void flush(int pc) {
int pc = codeAttr.code_length;
writeLocalVariables(pc, NoteKind.END); writeLocalVariables(pc, NoteKind.END);
} }
public void writeLocalVariables(int pc, NoteKind kind) { public void writeLocalVariables(int pc, NoteKind kind) {
ConstantPool constant_pool = classWriter.getClassFile().constant_pool;
String indent = space(2); // get from Options? String indent = space(2); // get from Options?
List<LocalVariableTypeTable_attribute.Entry> entries = pcMap.get(pc); var entries = pcMap.get(pc);
if (entries != null) { if (entries != null) {
for (ListIterator<LocalVariableTypeTable_attribute.Entry> iter = for (var iter = entries.listIterator(kind == NoteKind.END ? entries.size() : 0);
entries.listIterator(kind == NoteKind.END ? entries.size() : 0);
kind == NoteKind.END ? iter.hasPrevious() : iter.hasNext() ; ) { kind == NoteKind.END ? iter.hasPrevious() : iter.hasNext() ; ) {
LocalVariableTypeTable_attribute.Entry entry = var entry = kind == NoteKind.END ? iter.previous() : iter.next();
kind == NoteKind.END ? iter.previous() : iter.next();
if (kind.match(entry, pc)) { if (kind.match(entry, pc)) {
print(indent); print(indent);
print(kind.text); print(kind.text);
print(" generic local "); print(" generic local ");
print(entry.index); print(entry.slot());
print(" // "); print(" // ");
Descriptor d = new Signature(entry.signature_index);
try { try {
print(d.getFieldType(constant_pool).replace("/", ".")); print(classWriter.sigPrinter.print(Signature.parseFrom(
} catch (InvalidDescriptor e) { entry.signature().stringValue())).replace("/", "."));
print(report(e)); } catch (Exception e) {
} catch (ConstantPoolException e) {
print(report(e)); print(report(e));
} }
print(" "); print(" ");
try { print(entry.name().stringValue());
print(constant_pool.getUTF8Value(entry.name_index));
} catch (ConstantPoolException e) {
print(report(e));
}
println(); println();
} }
} }
} }
} }
private void put(int pc, LocalVariableTypeTable_attribute.Entry entry) { private void put(int pc, LocalVariableTypeInfo entry) {
List<LocalVariableTypeTable_attribute.Entry> list = pcMap.get(pc); var list = pcMap.get(pc);
if (list == null) { if (list == null) {
list = new ArrayList<>(); list = new ArrayList<>();
pcMap.put(pc, list); pcMap.put(pc, list);
@ -154,6 +140,6 @@ public class LocalVariableTypeTableWriter extends InstructionDetailWriter {
} }
private ClassWriter classWriter; private ClassWriter classWriter;
private Code_attribute codeAttr; private CodeModel codeAttr;
private Map<Integer, List<LocalVariableTypeTable_attribute.Entry>> pcMap; private Map<Integer, List<LocalVariableTypeInfo>> pcMap;
} }

View file

@ -29,7 +29,7 @@ import java.util.EnumSet;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import com.sun.tools.classfile.AccessFlags; import static jdk.internal.classfile.Classfile.*;
/* /*
* Provides access to javap's options, set via the command line * Provides access to javap's options, set via the command line
@ -54,16 +54,16 @@ public class Options {
/** /**
* Checks access of class, field or method. * Checks access of class, field or method.
*/ */
public boolean checkAccess(AccessFlags flags){ public boolean checkAccess(int flags){
boolean isPublic = flags.is(AccessFlags.ACC_PUBLIC); boolean isPublic = (flags & ACC_PUBLIC) != 0;
boolean isProtected = flags.is(AccessFlags.ACC_PROTECTED); boolean isProtected = (flags & ACC_PROTECTED) != 0;
boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE); boolean isPrivate = (flags & ACC_PRIVATE) != 0;
boolean isPackage = !(isPublic || isProtected || isPrivate); boolean isPackage = !(isPublic || isProtected || isPrivate);
if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage)) if ((showAccess == ACC_PUBLIC) && (isProtected || isPrivate || isPackage))
return false; return false;
else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage)) else if ((showAccess == ACC_PROTECTED) && (isPrivate || isPackage))
return false; return false;
else if ((showAccess == 0) && (isPrivate)) else if ((showAccess == 0) && (isPrivate))
return false; return false;

View file

@ -30,7 +30,6 @@ import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeMap; import java.util.TreeMap;
@ -40,13 +39,10 @@ import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
import javax.tools.StandardLocation; import javax.tools.StandardLocation;
import com.sun.tools.classfile.Attribute; import jdk.internal.classfile.Attributes;
import com.sun.tools.classfile.ClassFile; import jdk.internal.classfile.ClassModel;
import com.sun.tools.classfile.Code_attribute; import jdk.internal.classfile.CodeModel;
import com.sun.tools.classfile.ConstantPoolException; import jdk.internal.classfile.Instruction;
import com.sun.tools.classfile.Instruction;
import com.sun.tools.classfile.LineNumberTable_attribute;
import com.sun.tools.classfile.SourceFile_attribute;
/** /**
@ -74,14 +70,15 @@ public class SourceWriter extends InstructionDetailWriter {
this.fileManager = fileManager; this.fileManager = fileManager;
} }
public void reset(ClassFile cf, Code_attribute attr) { public void reset(CodeModel attr) {
setSource(cf); setSource(attr.parent().get().parent().get());
setLineMap(attr); setLineMap(attr);
} }
public void writeDetails(Instruction instr) { @Override
public void writeDetails(int pc, Instruction instr) {
String indent = space(40); // could get from Options? String indent = space(40); // could get from Options?
Set<Integer> lines = lineMap.get(instr.getPC()); var lines = lineMap.get(pc);
if (lines != null) { if (lines != null) {
for (int line: lines) { for (int line: lines) {
print(indent); print(indent);
@ -105,15 +102,13 @@ public class SourceWriter extends InstructionDetailWriter {
return (sourceLines.length > 0); return (sourceLines.length > 0);
} }
private void setLineMap(Code_attribute attr) { private void setLineMap(CodeModel attr) {
SortedMap<Integer, SortedSet<Integer>> map = new TreeMap<>(); SortedMap<Integer, SortedSet<Integer>> map = new TreeMap<>();
SortedSet<Integer> allLines = new TreeSet<>(); SortedSet<Integer> allLines = new TreeSet<>();
for (Attribute a: attr.attributes) { for (var t : attr.findAttributes(Attributes.LINE_NUMBER_TABLE)) {
if (a instanceof LineNumberTable_attribute) { for (var e: t.lineNumbers()) {
LineNumberTable_attribute t = (LineNumberTable_attribute) a; int start_pc = e.startPc();
for (LineNumberTable_attribute.Entry e: t.line_number_table) { int line = e.lineNumber();
int start_pc = e.start_pc;
int line = e.line_number;
SortedSet<Integer> pcLines = map.get(start_pc); SortedSet<Integer> pcLines = map.get(start_pc);
if (pcLines == null) { if (pcLines == null) {
pcLines = new TreeSet<>(); pcLines = new TreeSet<>();
@ -123,19 +118,18 @@ public class SourceWriter extends InstructionDetailWriter {
allLines.add(line); allLines.add(line);
} }
} }
}
lineMap = map; lineMap = map;
lineList = new ArrayList<>(allLines); lineList = new ArrayList<>(allLines);
} }
private void setSource(ClassFile cf) { private void setSource(ClassModel cf) {
if (cf != classFile) { if (cf != classFile) {
classFile = cf; classFile = cf;
sourceLines = splitLines(readSource(cf)); sourceLines = splitLines(readSource(cf));
} }
} }
private String readSource(ClassFile cf) { private String readSource(ClassModel cf) {
if (fileManager == null) if (fileManager == null)
return null; return null;
@ -150,14 +144,13 @@ public class SourceWriter extends InstructionDetailWriter {
// additional classes to determine the outmost class from any // additional classes to determine the outmost class from any
// InnerClasses and EnclosingMethod attributes. // InnerClasses and EnclosingMethod attributes.
try { try {
String className = cf.getName(); String className = cf.thisClass().asInternalName();
SourceFile_attribute sf = var sf = cf.findAttribute(Attributes.SOURCE_FILE);
(SourceFile_attribute) cf.attributes.get(Attribute.SourceFile); if (sf.isEmpty()) {
if (sf == null) {
report(messages.getMessage("err.no.SourceFile.attribute")); report(messages.getMessage("err.no.SourceFile.attribute"));
return null; return null;
} }
String sourceFile = sf.getSourceFile(cf.constant_pool); String sourceFile = sf.get().sourceFile().stringValue();
String fileBase = sourceFile.endsWith(".java") String fileBase = sourceFile.endsWith(".java")
? sourceFile.substring(0, sourceFile.length() - 5) : sourceFile; ? sourceFile.substring(0, sourceFile.length() - 5) : sourceFile;
int sep = className.lastIndexOf("/"); int sep = className.lastIndexOf("/");
@ -172,9 +165,6 @@ public class SourceWriter extends InstructionDetailWriter {
return null; return null;
} }
return fo.getCharContent(true).toString(); return fo.getCharContent(true).toString();
} catch (ConstantPoolException e) {
report(e);
return null;
} catch (IOException e) { } catch (IOException e) {
report(e.getLocalizedMessage()); report(e.getLocalizedMessage());
return null; return null;
@ -205,7 +195,7 @@ public class SourceWriter extends InstructionDetailWriter {
} }
private JavaFileManager fileManager; private JavaFileManager fileManager;
private ClassFile classFile; private ClassModel classFile;
private SortedMap<Integer, SortedSet<Integer>> lineMap; private SortedMap<Integer, SortedSet<Integer>> lineMap;
private List<Integer> lineList; private List<Integer> lineList;
private String[] sourceLines; private String[] sourceLines;

View file

@ -25,23 +25,16 @@
package com.sun.tools.javap; package com.sun.tools.javap;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import jdk.internal.classfile.Attributes;
import jdk.internal.classfile.Classfile;
import com.sun.tools.classfile.AccessFlags; import jdk.internal.classfile.Instruction;
import com.sun.tools.classfile.Attribute; import jdk.internal.classfile.attribute.CodeAttribute;
import com.sun.tools.classfile.Code_attribute; import jdk.internal.classfile.attribute.StackMapFrameInfo;
import com.sun.tools.classfile.ConstantPool; import jdk.internal.classfile.attribute.StackMapTableAttribute;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Descriptor;
import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
import com.sun.tools.classfile.Instruction;
import com.sun.tools.classfile.Method;
import com.sun.tools.classfile.StackMapTable_attribute;
import com.sun.tools.classfile.StackMapTable_attribute.*;
import static com.sun.tools.classfile.StackMapTable_attribute.verification_type_info.*;
/** /**
* Annotate instructions with stack map. * Annotate instructions with stack map.
@ -62,228 +55,108 @@ public class StackMapWriter extends InstructionDetailWriter {
protected StackMapWriter(Context context) { protected StackMapWriter(Context context) {
super(context); super(context);
context.put(StackMapWriter.class, this); context.put(StackMapWriter.class, this);
classWriter = ClassWriter.instance(context);
} }
public void reset(Code_attribute attr) { public void reset(CodeAttribute code) {
setStackMap((StackMapTable_attribute) attr.attributes.get(Attribute.StackMapTable)); setStackMap(code);
} }
void setStackMap(StackMapTable_attribute attr) { void setStackMap(CodeAttribute code) {
StackMapTableAttribute attr = code.findAttribute(Attributes.STACK_MAP_TABLE)
.orElse(null);
if (attr == null) { if (attr == null) {
map = null; map = null;
return; return;
} }
var m = code.parent().get();
Method m = classWriter.getMethod(); if ((m.flags().flagsMask() & Classfile.ACC_STATIC) == 0) {
Descriptor d = m.descriptor; thisClassName = m.parent().get().thisClass().asInternalName();
String[] args; } else {
try { thisClassName = null;
ConstantPool cp = classWriter.getClassFile().constant_pool;
String argString = d.getParameterTypes(cp);
args = argString.substring(1, argString.length() - 1).split("[, ]+");
} catch (ConstantPoolException | InvalidDescriptor e) {
return;
}
boolean isStatic = m.access_flags.is(AccessFlags.ACC_STATIC);
verification_type_info[] initialLocals = new verification_type_info[(isStatic ? 0 : 1) + args.length];
if (!isStatic)
initialLocals[0] = new CustomVerificationTypeInfo("this");
for (int i = 0; i < args.length; i++) {
initialLocals[(isStatic ? 0 : 1) + i] =
new CustomVerificationTypeInfo(args[i].replace(".", "/"));
} }
map = new HashMap<>(); map = new HashMap<>();
StackMapBuilder builder = new StackMapBuilder(); this.code = code;
for (var fr : attr.entries())
// using -1 as the pc for the initial frame effectively compensates for map.put(code.labelToBci(fr.target()), fr);
// the difference in behavior for the first stack map frame (where the
// pc offset is just offset_delta) compared to subsequent frames (where
// the pc offset is always offset_delta+1).
int pc = -1;
map.put(pc, new StackMap(initialLocals, empty));
for (int i = 0; i < attr.entries.length; i++)
pc = attr.entries[i].accept(builder, pc);
} }
public void writeInitialDetails() { public void writeInitialDetails() {
writeDetails(-1); writeDetails(-1);
} }
public void writeDetails(Instruction instr) { @Override
writeDetails(instr.getPC()); public void writeDetails(int pc, Instruction instr) {
writeDetails(pc);
} }
private void writeDetails(int pc) { private void writeDetails(int pc) {
if (map == null) if (map == null)
return; return;
StackMap m = map.get(pc); var m = map.get(pc);
if (m != null) { if (m != null) {
print("StackMap locals: ", m.locals); print("StackMap locals: ", m.locals(), true);
print("StackMap stack: ", m.stack); print("StackMap stack: ", m.stack(), false);
} }
} }
void print(String label, verification_type_info[] entries) { void print(String label, List<StackMapFrameInfo.VerificationTypeInfo> entries,
boolean firstThis) {
print(label); print(label);
for (int i = 0; i < entries.length; i++) { for (var e : entries) {
print(" "); print(" ");
print(entries[i]); print(e, firstThis);
firstThis = false;
} }
println(); println();
} }
void print(verification_type_info entry) { void print(StackMapFrameInfo.VerificationTypeInfo entry, boolean firstThis) {
if (entry == null) { if (entry == null) {
print("ERROR"); print("ERROR");
return; return;
} }
switch (entry.tag) { switch (entry) {
case -1: case StackMapFrameInfo.SimpleVerificationTypeInfo s -> {
print(((CustomVerificationTypeInfo) entry).text); switch (s) {
break; case ITEM_TOP ->
case ITEM_Top:
print("top"); print("top");
break;
case ITEM_Integer: case ITEM_INTEGER ->
print("int"); print("int");
break;
case ITEM_Float: case ITEM_FLOAT ->
print("float"); print("float");
break;
case ITEM_Long: case ITEM_LONG ->
print("long"); print("long");
break;
case ITEM_Double: case ITEM_DOUBLE ->
print("double"); print("double");
break;
case ITEM_Null: case ITEM_NULL ->
print("null"); print("null");
break;
case ITEM_UninitializedThis: case ITEM_UNINITIALIZED_THIS ->
print("uninit_this"); print("uninit_this");
break;
case ITEM_Object:
try {
ConstantPool cp = classWriter.getClassFile().constant_pool;
ConstantPool.CONSTANT_Class_info class_info = cp.getClassInfo(((Object_variable_info) entry).cpool_index);
print(cp.getUTF8Value(class_info.name_index));
} catch (ConstantPoolException e) {
print("??");
} }
break; }
case ITEM_Uninitialized: case StackMapFrameInfo.ObjectVerificationTypeInfo o -> {
print(((Uninitialized_variable_info) entry).offset); String cln = o.className().asInternalName();
break; print(firstThis && cln.equals(thisClassName) ? "this" : cln);
}
case StackMapFrameInfo.UninitializedVerificationTypeInfo u ->
print(code.labelToBci(u.newTarget()));
} }
} }
private Map<Integer, StackMap> map; private Map<Integer, StackMapFrameInfo> map;
private ClassWriter classWriter; private String thisClassName;
private CodeAttribute code;
class StackMapBuilder
implements StackMapTable_attribute.stack_map_frame.Visitor<Integer, Integer> {
public Integer visit_same_frame(same_frame frame, Integer pc) {
int new_pc = pc + frame.getOffsetDelta() + 1;
StackMap m = map.get(pc);
assert (m != null);
map.put(new_pc, m);
return new_pc;
}
public Integer visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame frame, Integer pc) {
int new_pc = pc + frame.getOffsetDelta() + 1;
StackMap prev = map.get(pc);
assert (prev != null);
StackMap m = new StackMap(prev.locals, frame.stack);
map.put(new_pc, m);
return new_pc;
}
public Integer visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended frame, Integer pc) {
int new_pc = pc + frame.getOffsetDelta() + 1;
StackMap prev = map.get(pc);
assert (prev != null);
StackMap m = new StackMap(prev.locals, frame.stack);
map.put(new_pc, m);
return new_pc;
}
public Integer visit_chop_frame(chop_frame frame, Integer pc) {
int new_pc = pc + frame.getOffsetDelta() + 1;
StackMap prev = map.get(pc);
assert (prev != null);
int k = 251 - frame.frame_type;
verification_type_info[] new_locals = Arrays.copyOf(prev.locals, prev.locals.length - k);
StackMap m = new StackMap(new_locals, empty);
map.put(new_pc, m);
return new_pc;
}
public Integer visit_same_frame_extended(same_frame_extended frame, Integer pc) {
int new_pc = pc + frame.getOffsetDelta();
StackMap m = map.get(pc);
assert (m != null);
map.put(new_pc, m);
return new_pc;
}
public Integer visit_append_frame(append_frame frame, Integer pc) {
int new_pc = pc + frame.getOffsetDelta() + 1;
StackMap prev = map.get(pc);
assert (prev != null);
verification_type_info[] new_locals = new verification_type_info[prev.locals.length + frame.locals.length];
System.arraycopy(prev.locals, 0, new_locals, 0, prev.locals.length);
System.arraycopy(frame.locals, 0, new_locals, prev.locals.length, frame.locals.length);
StackMap m = new StackMap(new_locals, empty);
map.put(new_pc, m);
return new_pc;
}
public Integer visit_full_frame(full_frame frame, Integer pc) {
int new_pc = pc + frame.getOffsetDelta() + 1;
StackMap m = new StackMap(frame.locals, frame.stack);
map.put(new_pc, m);
return new_pc;
}
}
static class StackMap {
StackMap(verification_type_info[] locals, verification_type_info[] stack) {
this.locals = locals;
this.stack = stack;
}
private final verification_type_info[] locals;
private final verification_type_info[] stack;
}
static class CustomVerificationTypeInfo extends verification_type_info {
public CustomVerificationTypeInfo(String text) {
super(-1);
this.text = text;
}
private String text;
}
private final verification_type_info[] empty = { };
} }

View file

@ -25,14 +25,13 @@
package com.sun.tools.javap; package com.sun.tools.javap;
import com.sun.tools.classfile.Code_attribute;
import com.sun.tools.classfile.Code_attribute.Exception_data;
import com.sun.tools.classfile.Instruction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import jdk.internal.classfile.Instruction;
import jdk.internal.classfile.instruction.ExceptionCatch;
import jdk.internal.classfile.attribute.CodeAttribute;
/** /**
* Annotate instructions with details about try blocks. * Annotate instructions with details about try blocks.
@ -45,24 +44,24 @@ import java.util.Map;
public class TryBlockWriter extends InstructionDetailWriter { public class TryBlockWriter extends InstructionDetailWriter {
public enum NoteKind { public enum NoteKind {
START("try") { START("try") {
public boolean match(Exception_data entry, int pc) { public boolean match(ExceptionCatch entry, int pc, CodeAttribute lr) {
return (pc == entry.start_pc); return (pc == lr.labelToBci(entry.tryStart()));
} }
}, },
END("end try") { END("end try") {
public boolean match(Exception_data entry, int pc) { public boolean match(ExceptionCatch entry, int pc, CodeAttribute lr) {
return (pc == entry.end_pc); return (pc == lr.labelToBci(entry.tryEnd()));
} }
}, },
HANDLER("catch") { HANDLER("catch") {
public boolean match(Exception_data entry, int pc) { public boolean match(ExceptionCatch entry, int pc, CodeAttribute lr) {
return (pc == entry.handler_pc); return (pc == lr.labelToBci(entry.handler()));
} }
}; };
NoteKind(String text) { NoteKind(String text) {
this.text = text; this.text = text;
} }
public abstract boolean match(Exception_data entry, int pc); public abstract boolean match(ExceptionCatch entry, int pc, CodeAttribute lr);
public final String text; public final String text;
} }
@ -79,46 +78,49 @@ public class TryBlockWriter extends InstructionDetailWriter {
constantWriter = ConstantWriter.instance(context); constantWriter = ConstantWriter.instance(context);
} }
public void reset(Code_attribute attr) { public void reset(CodeAttribute attr) {
indexMap = new HashMap<>(); indexMap = new HashMap<>();
pcMap = new HashMap<>(); pcMap = new HashMap<>();
for (int i = 0; i < attr.exception_table.length; i++) { lr = attr;
Exception_data entry = attr.exception_table[i]; var excs = attr.exceptionHandlers();
for (int i = 0; i < excs.size(); i++) {
var entry = excs.get(i);
indexMap.put(entry, i); indexMap.put(entry, i);
put(entry.start_pc, entry); put(lr.labelToBci(entry.tryStart()), entry);
put(entry.end_pc, entry); put(lr.labelToBci(entry.tryEnd()), entry);
put(entry.handler_pc, entry); put(lr.labelToBci(entry.handler()), entry);
} }
} }
public void writeDetails(Instruction instr) { @Override
writeTrys(instr, NoteKind.END); public void writeDetails(int pc, Instruction instr) {
writeTrys(instr, NoteKind.START); writeTrys(pc, instr, NoteKind.END);
writeTrys(instr, NoteKind.HANDLER); writeTrys(pc, instr, NoteKind.START);
writeTrys(pc, instr, NoteKind.HANDLER);
} }
public void writeTrys(Instruction instr, NoteKind kind) { public void writeTrys(int pc, Instruction instr, NoteKind kind) {
String indent = space(2); // get from Options? String indent = space(2); // get from Options?
int pc = instr.getPC(); var entries = pcMap.get(pc);
List<Exception_data> entries = pcMap.get(pc);
if (entries != null) { if (entries != null) {
for (ListIterator<Exception_data> iter = for (var iter =
entries.listIterator(kind == NoteKind.END ? entries.size() : 0); entries.listIterator(kind == NoteKind.END ? entries.size() : 0);
kind == NoteKind.END ? iter.hasPrevious() : iter.hasNext() ; ) { kind == NoteKind.END ? iter.hasPrevious() : iter.hasNext() ; ) {
Exception_data entry = var entry =
kind == NoteKind.END ? iter.previous() : iter.next(); kind == NoteKind.END ? iter.previous() : iter.next();
if (kind.match(entry, pc)) { if (kind.match(entry, pc, lr)) {
print(indent); print(indent);
print(kind.text); print(kind.text);
print("["); print("[");
print(indexMap.get(entry)); print(indexMap.get(entry));
print("] "); print("] ");
if (entry.catch_type == 0) var ct = entry.catchType();
if (ct.isEmpty())
print("finally"); print("finally");
else { else {
print("#" + entry.catch_type); print("#" + ct.get().index());
print(" // "); print(" // ");
constantWriter.write(entry.catch_type); constantWriter.write(ct.get().index());
} }
println(); println();
} }
@ -126,8 +128,8 @@ public class TryBlockWriter extends InstructionDetailWriter {
} }
} }
private void put(int pc, Exception_data entry) { private void put(int pc, ExceptionCatch entry) {
List<Exception_data> list = pcMap.get(pc); var list = pcMap.get(pc);
if (list == null) { if (list == null) {
list = new ArrayList<>(); list = new ArrayList<>();
pcMap.put(pc, list); pcMap.put(pc, list);
@ -136,7 +138,8 @@ public class TryBlockWriter extends InstructionDetailWriter {
list.add(entry); list.add(entry);
} }
private Map<Integer, List<Exception_data>> pcMap; private Map<Integer, List<ExceptionCatch>> pcMap;
private Map<Exception_data, Integer> indexMap; private Map<ExceptionCatch, Integer> indexMap;
private ConstantWriter constantWriter; private ConstantWriter constantWriter;
private CodeAttribute lr;
} }

View file

@ -25,20 +25,17 @@
package com.sun.tools.javap; package com.sun.tools.javap;
import com.sun.tools.classfile.Attribute;
import com.sun.tools.classfile.Code_attribute;
import com.sun.tools.classfile.TypeAnnotation;
import com.sun.tools.classfile.TypeAnnotation.Position;
import com.sun.tools.classfile.Instruction;
import com.sun.tools.classfile.Method;
import com.sun.tools.classfile.RuntimeInvisibleTypeAnnotations_attribute;
import com.sun.tools.classfile.RuntimeTypeAnnotations_attribute;
import com.sun.tools.classfile.RuntimeVisibleTypeAnnotations_attribute;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import com.sun.tools.javac.util.StringUtils; import java.util.Optional;
import jdk.internal.classfile.Attributes;
import jdk.internal.classfile.Instruction;
import jdk.internal.classfile.MethodModel;
import jdk.internal.classfile.TypeAnnotation;
import jdk.internal.classfile.attribute.CodeAttribute;
/** /**
* Annotate instructions with details about type annotations. * Annotate instructions with details about type annotations.
@ -74,29 +71,38 @@ public class TypeAnnotationWriter extends InstructionDetailWriter {
classWriter = ClassWriter.instance(context); classWriter = ClassWriter.instance(context);
} }
public void reset(Code_attribute attr) { public void reset(CodeAttribute attr) {
Method m = classWriter.getMethod(); MethodModel m = attr.parent().get();
pcMap = new HashMap<>(); pcMap = new HashMap<>();
check(NoteKind.VISIBLE, (RuntimeVisibleTypeAnnotations_attribute) m.attributes.get(Attribute.RuntimeVisibleTypeAnnotations)); codeAttribute = attr;
check(NoteKind.INVISIBLE, (RuntimeInvisibleTypeAnnotations_attribute) m.attributes.get(Attribute.RuntimeInvisibleTypeAnnotations)); check(NoteKind.VISIBLE,
m.findAttribute(Attributes.RUNTIME_VISIBLE_TYPE_ANNOTATIONS)
.map(a -> a.annotations()));
check(NoteKind.INVISIBLE,
m.findAttribute(Attributes.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS)
.map(a -> a.annotations()));
} }
private void check(NoteKind kind, RuntimeTypeAnnotations_attribute attr) { private void check(NoteKind kind, Optional<List<TypeAnnotation>> annos) {
if (attr == null) if (annos.isEmpty())
return; return;
for (TypeAnnotation anno: attr.annotations) { for (TypeAnnotation anno: annos.get()) {
Position p = anno.position; switch (anno.targetInfo()) {
case TypeAnnotation.LocalVarTarget p -> {
Note note = null; Note note = null;
if (p.offset != -1) for (var lvar : p.table()) {
addNote(p.offset, note = new Note(kind, anno));
if (p.lvarOffset != null) {
for (int i = 0; i < p.lvarOffset.length; i++) {
if (note == null) if (note == null)
note = new Note(kind, anno); note = new Note(kind, anno);
addNote(p.lvarOffset[i], note); addNote(codeAttribute.labelToBci(lvar.startLabel()), note);
} }
} }
case TypeAnnotation.OffsetTarget p ->
addNote(codeAttribute.labelToBci(p.target()), new Note(kind, anno));
case TypeAnnotation.TypeArgumentTarget p ->
addNote(codeAttribute.labelToBci(p.target()), new Note(kind, anno));
default -> {}
}
} }
} }
@ -108,17 +114,16 @@ public class TypeAnnotationWriter extends InstructionDetailWriter {
} }
@Override @Override
void writeDetails(Instruction instr) { void writeDetails(int pc, Instruction instr) {
String indent = space(2); // get from Options? String indent = space(2); // get from Options?
int pc = instr.getPC();
List<Note> notes = pcMap.get(pc); List<Note> notes = pcMap.get(pc);
if (notes != null) { if (notes != null) {
for (Note n: notes) { for (Note n: notes) {
print(indent); print(indent);
print("@"); print("@");
annotationWriter.write(n.anno, false, true); annotationWriter.write(n.anno, false, true, codeAttribute);
print(", "); print(", ");
println(StringUtils.toLowerCase(n.kind.toString())); println(n.kind.toString().toLowerCase(Locale.US));
} }
} }
} }
@ -126,4 +131,5 @@ public class TypeAnnotationWriter extends InstructionDetailWriter {
private AnnotationWriter annotationWriter; private AnnotationWriter annotationWriter;
private ClassWriter classWriter; private ClassWriter classWriter;
private Map<Integer, List<Note>> pcMap; private Map<Integer, List<Note>> pcMap;
private CodeAttribute codeAttribute;
} }

View file

@ -130,6 +130,7 @@ serviceability/sa/ClhsdbPmap.java#core 8267433 macosx-x64
serviceability/sa/ClhsdbPstack.java#core 8267433 macosx-x64 serviceability/sa/ClhsdbPstack.java#core 8267433 macosx-x64
serviceability/sa/TestJmapCore.java 8267433 macosx-x64 serviceability/sa/TestJmapCore.java 8267433 macosx-x64
serviceability/sa/TestJmapCoreMetaspace.java 8267433 macosx-x64 serviceability/sa/TestJmapCoreMetaspace.java 8267433 macosx-x64
serviceability/sa/ClhsdbDumpclass.java 8316342 generic-all
serviceability/attach/ConcAttachTest.java 8290043 linux-all serviceability/attach/ConcAttachTest.java 8290043 linux-all

View file

@ -30,13 +30,21 @@
* @modules jdk.jdeps/com.sun.tools.javap * @modules jdk.jdeps/com.sun.tools.javap
*/ */
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter;
public class T8260403 { public class T8260403 {
public static void main(String args[]) throws Exception { public static void main(String args[]) throws Exception {
if (com.sun.tools.javap.Main.run(new String[]{"-c", System.getProperty("test.classes") + "/InvalidSignature.class"}, var sw = new StringWriter();
new PrintWriter(System.out)) != 0) { int res = com.sun.tools.javap.Main.run(
throw new AssertionError(); new String[]{"-c", System.getProperty("test.classes") + "/InvalidSignature.class"},
} new PrintWriter(sw));
System.out.println(sw);
if (res == 0)
throw new AssertionError("Failure exit code expected");
if (sw.toString().contains("Fatal error"))
throw new AssertionError("Unguarded fatal error");
if (sw.toString().contains("error while reading constant pool"))
throw new AssertionError("Unguarded constant pool error");
} }
} }

View file

@ -60,7 +60,9 @@ public class T6866657
JavapTask t = new JavapTask(log, fileManager, null); JavapTask t = new JavapTask(log, fileManager, null);
t.handleOptions(new String[] { "-sysinfo", className }); t.handleOptions(new String[] { "-sysinfo", className });
JavapTask.ClassFileInfo cfInfo = t.read(fo); JavapTask.ClassFileInfo cfInfo = t.read(fo);
expectEqual(cfInfo.cf.byteLength(), cfInfo.size); try (var in = fo.openInputStream()) {
expectEqual(in.readAllBytes().length, cfInfo.size);
}
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -56,7 +56,9 @@ public class T7186925
JavapTask t = new JavapTask(null, fileManager, null); JavapTask t = new JavapTask(null, fileManager, null);
t.handleOptions(new String[] { "-sysinfo", className }); t.handleOptions(new String[] { "-sysinfo", className });
JavapTask.ClassFileInfo cfInfo = t.read(fo); JavapTask.ClassFileInfo cfInfo = t.read(fo);
expectEqual(cfInfo.cf.byteLength(), cfInfo.size); try (var in = fo.openInputStream()) {
expectEqual(in.readAllBytes().length, cfInfo.size);
}
} }
} catch (NullPointerException ee) { } catch (NullPointerException ee) {
ee.printStackTrace(); ee.printStackTrace();

View file

@ -0,0 +1,54 @@
class Malformed {
0xCAFEBABE;
0; // minor version
0; // version
[] { // Constant Pool
; // first element is empty
Utf8 "Code"; // #1
Method #0 #0; // #2
class #0; // #3
} // Constant Pool
0x0000; // access
#0;// this_cpx
#3;// super_cpx
[] { // Interfaces
#0;
#3;
} // Interfaces
[] { // fields
{ // Member
0x0000; // access
#0; // name_cpx
#0; // sig_cpx
[] { // Attributes
} // Attributes
} // Member
} // fields
[] { // methods
{ // Member
0x0000; // access
#0; // name_cpx
#0; // sig_cpx
[] { // Attributes
Attr(#1) { // Code
0; // max_stack
0; // max_locals
Bytes[]{
}
[] { // Traps
} // end Traps
[] { // Attributes
} // Attributes
} // end Code
} // Attributes
} // Member
} // methods
[] { // Attributes
} // Attributes
} // end class Malformed

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2023, 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.
*/
/*
* @test
* @bug 8294969
* @summary javap test safeguarding malformed class file
* @build Malformed
* @run main MalformedTest
* @modules jdk.jdeps/com.sun.tools.javap
*/
import java.io.PrintWriter;
import java.io.StringWriter;
public class MalformedTest {
public static void main(String args[]) throws Exception {
var sw = new StringWriter();
int res = com.sun.tools.javap.Main.run(
new String[]{"-c", "-v", System.getProperty("test.classes") + "/Malformed.class"},
new PrintWriter(sw));
System.out.println(sw);
if (res == 0)
throw new AssertionError("Failure exit code expected");
if (sw.toString().contains("Fatal error"))
throw new AssertionError("Unguarded fatal error");
if (sw.toString().contains("error while reading constant pool"))
throw new AssertionError("Unguarded constant pool error");
}
}