8294969: Convert jdk.jdeps javap to use the Classfile API
Reviewed-by: vromero
This commit is contained in:
parent
fbc766ee21
commit
1203e11a8d
@ -102,6 +102,16 @@ public sealed interface ClassReader extends ConstantPool
|
||||
*/
|
||||
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
|
||||
* offset within the classfile, or null if the index at the specified
|
||||
|
@ -38,6 +38,11 @@ public sealed interface DynamicConstantPoolEntry extends PoolEntry
|
||||
*/
|
||||
BootstrapMethodEntry bootstrap();
|
||||
|
||||
/**
|
||||
* {@return index of the entry in the bootstrap method table for this constant}
|
||||
*/
|
||||
int bootstrapMethodIndex();
|
||||
|
||||
/**
|
||||
* {@return the invocation name and type}
|
||||
*/
|
||||
|
@ -383,7 +383,7 @@ public abstract sealed class AbstractInstruction
|
||||
@Override
|
||||
public FieldRefEntry field() {
|
||||
if (fieldEntry == null)
|
||||
fieldEntry = (FieldRefEntry) code.classReader.readEntry(pos + 1);
|
||||
fieldEntry = code.classReader.readEntry(pos + 1, FieldRefEntry.class);
|
||||
return fieldEntry;
|
||||
}
|
||||
|
||||
@ -413,7 +413,7 @@ public abstract sealed class AbstractInstruction
|
||||
@Override
|
||||
public MemberRefEntry method() {
|
||||
if (methodEntry == null)
|
||||
methodEntry = (MemberRefEntry) code.classReader.readEntry(pos + 1);
|
||||
methodEntry = code.classReader.readEntry(pos + 1, MemberRefEntry.class);
|
||||
return methodEntry;
|
||||
}
|
||||
|
||||
@ -453,7 +453,7 @@ public abstract sealed class AbstractInstruction
|
||||
@Override
|
||||
public MemberRefEntry method() {
|
||||
if (methodEntry == null)
|
||||
methodEntry = (InterfaceMethodRefEntry) code.classReader.readEntry(pos + 1);
|
||||
methodEntry = code.classReader.readEntry(pos + 1, InterfaceMethodRefEntry.class);
|
||||
return methodEntry;
|
||||
}
|
||||
|
||||
@ -493,7 +493,7 @@ public abstract sealed class AbstractInstruction
|
||||
@Override
|
||||
public InvokeDynamicEntry invokedynamic() {
|
||||
if (indyEntry == null)
|
||||
indyEntry = (InvokeDynamicEntry) code.classReader.readEntry(pos + 1);
|
||||
indyEntry = code.classReader.readEntry(pos + 1, InvokeDynamicEntry.class);
|
||||
return indyEntry;
|
||||
}
|
||||
|
||||
|
@ -815,6 +815,13 @@ public abstract sealed class AbstractPoolEntry {
|
||||
return bootstrapMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the bsmIndex
|
||||
*/
|
||||
public int bootstrapMethodIndex() {
|
||||
return bsmIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the nameAndType
|
||||
*/
|
||||
|
@ -58,14 +58,14 @@ class AnnotationReader {
|
||||
char tag = (char) classReader.readU1(p);
|
||||
++p;
|
||||
return switch (tag) {
|
||||
case AEV_BYTE -> new AnnotationImpl.OfByteImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_CHAR -> new AnnotationImpl.OfCharacterImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_DOUBLE -> new AnnotationImpl.OfDoubleImpl((DoubleEntry)classReader.readEntry(p));
|
||||
case AEV_FLOAT -> new AnnotationImpl.OfFloatImpl((FloatEntry)classReader.readEntry(p));
|
||||
case AEV_INT -> new AnnotationImpl.OfIntegerImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_LONG -> new AnnotationImpl.OfLongImpl((LongEntry)classReader.readEntry(p));
|
||||
case AEV_SHORT -> new AnnotationImpl.OfShortImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_BOOLEAN -> new AnnotationImpl.OfBooleanImpl((IntegerEntry)classReader.readEntry(p));
|
||||
case AEV_BYTE -> new AnnotationImpl.OfByteImpl(classReader.readEntry(p, IntegerEntry.class));
|
||||
case AEV_CHAR -> new AnnotationImpl.OfCharacterImpl(classReader.readEntry(p, IntegerEntry.class));
|
||||
case AEV_DOUBLE -> new AnnotationImpl.OfDoubleImpl(classReader.readEntry(p, DoubleEntry.class));
|
||||
case AEV_FLOAT -> new AnnotationImpl.OfFloatImpl(classReader.readEntry(p, FloatEntry.class));
|
||||
case AEV_INT -> new AnnotationImpl.OfIntegerImpl(classReader.readEntry(p, IntegerEntry.class));
|
||||
case AEV_LONG -> new AnnotationImpl.OfLongImpl(classReader.readEntry(p, LongEntry.class));
|
||||
case AEV_SHORT -> new AnnotationImpl.OfShortImpl(classReader.readEntry(p, IntegerEntry.class));
|
||||
case AEV_BOOLEAN -> new AnnotationImpl.OfBooleanImpl(classReader.readEntry(p, IntegerEntry.class));
|
||||
case AEV_STRING -> new AnnotationImpl.OfStringImpl(classReader.readUtf8Entry(p));
|
||||
case AEV_ENUM -> new AnnotationImpl.OfEnumImpl(classReader.readUtf8Entry(p), classReader.readUtf8Entry(p + 2));
|
||||
case AEV_CLASS -> new AnnotationImpl.OfClassImpl(classReader.readUtf8Entry(p));
|
||||
|
@ -483,7 +483,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
|
||||
|
||||
@Override
|
||||
public ConstantValueEntry constant() {
|
||||
return (ConstantValueEntry) classReader.readEntry(payloadStart);
|
||||
return classReader.readEntry(payloadStart, ConstantValueEntry.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ package jdk.internal.classfile.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
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.ConstantPoolException;
|
||||
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.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_STRING;
|
||||
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
|
||||
implements ClassReader {
|
||||
@ -156,7 +155,7 @@ public final class ClassReaderImpl
|
||||
@Override
|
||||
public ClassEntry thisClassEntry() {
|
||||
if (thisClass == null) {
|
||||
thisClass = readClassEntry(thisClassPos);
|
||||
thisClass = readEntry(thisClassPos, ClassEntry.class);
|
||||
}
|
||||
return thisClass;
|
||||
}
|
||||
@ -391,6 +390,13 @@ public final class ClassReaderImpl
|
||||
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
|
||||
public PoolEntry readEntryOrNull(int pos) {
|
||||
int index = readU2(pos);
|
||||
@ -417,32 +423,27 @@ public final class ClassReaderImpl
|
||||
|
||||
@Override
|
||||
public ModuleEntry readModuleEntry(int pos) {
|
||||
if (readEntry(pos) instanceof ModuleEntry me) return me;
|
||||
throw new ConstantPoolException("Not a module entry at pos: " + pos);
|
||||
return readEntry(pos, ModuleEntry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageEntry readPackageEntry(int pos) {
|
||||
if (readEntry(pos) instanceof PackageEntry pe) return pe;
|
||||
throw new ConstantPoolException("Not a package entry at pos: " + pos);
|
||||
return readEntry(pos, PackageEntry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassEntry readClassEntry(int pos) {
|
||||
if (readEntry(pos) instanceof ClassEntry ce) return ce;
|
||||
throw new ConstantPoolException("Not a class entry at pos: " + pos);
|
||||
return readEntry(pos, ClassEntry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameAndTypeEntry readNameAndTypeEntry(int pos) {
|
||||
if (readEntry(pos) instanceof NameAndTypeEntry nate) return nate;
|
||||
throw new ConstantPoolException("Not a name and type entry at pos: " + pos);
|
||||
return readEntry(pos, NameAndTypeEntry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandleEntry readMethodHandleEntry(int pos) {
|
||||
if (readEntry(pos) instanceof MethodHandleEntry mhe) return mhe;
|
||||
throw new ConstantPoolException("Not a method handle entry at pos: " + pos);
|
||||
return readEntry(pos, MethodHandleEntry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -186,15 +186,19 @@ module java.base {
|
||||
java.logging;
|
||||
exports jdk.internal.classfile to
|
||||
jdk.jartool,
|
||||
jdk.jdeps,
|
||||
jdk.jlink,
|
||||
jdk.jshell;
|
||||
exports jdk.internal.classfile.attribute to
|
||||
jdk.jartool,
|
||||
jdk.jdeps,
|
||||
jdk.jlink;
|
||||
exports jdk.internal.classfile.constantpool to
|
||||
jdk.jartool,
|
||||
jdk.jdeps,
|
||||
jdk.jlink;
|
||||
exports jdk.internal.classfile.instruction to
|
||||
jdk.jdeps,
|
||||
jdk.jlink,
|
||||
jdk.jshell;
|
||||
exports jdk.internal.org.objectweb.asm to
|
||||
|
@ -25,17 +25,14 @@
|
||||
|
||||
package com.sun.tools.javap;
|
||||
|
||||
import com.sun.tools.classfile.Annotation;
|
||||
import com.sun.tools.classfile.TypeAnnotation;
|
||||
import com.sun.tools.classfile.Annotation.Annotation_element_value;
|
||||
import com.sun.tools.classfile.Annotation.Array_element_value;
|
||||
import com.sun.tools.classfile.Annotation.Class_element_value;
|
||||
import com.sun.tools.classfile.Annotation.Enum_element_value;
|
||||
import com.sun.tools.classfile.Annotation.Primitive_element_value;
|
||||
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 java.util.List;
|
||||
import jdk.internal.classfile.Annotation;
|
||||
import jdk.internal.classfile.AnnotationElement;
|
||||
import jdk.internal.classfile.AnnotationValue;
|
||||
import jdk.internal.classfile.constantpool.*;
|
||||
import jdk.internal.classfile.Signature;
|
||||
import jdk.internal.classfile.TypeAnnotation;
|
||||
import jdk.internal.classfile.attribute.CodeAttribute;
|
||||
|
||||
/**
|
||||
* A writer for writing annotations as text.
|
||||
@ -68,15 +65,15 @@ public class AnnotationWriter extends BasicWriter {
|
||||
}
|
||||
|
||||
public void write(Annotation annot, boolean resolveIndices) {
|
||||
writeDescriptor(annot.type_index, resolveIndices);
|
||||
writeDescriptor(annot.className(), resolveIndices);
|
||||
if (resolveIndices) {
|
||||
boolean showParens = annot.num_element_value_pairs > 0;
|
||||
boolean showParens = annot.elements().size() > 0;
|
||||
if (showParens) {
|
||||
println("(");
|
||||
indent(+1);
|
||||
}
|
||||
for (int i = 0; i < annot.num_element_value_pairs; i++) {
|
||||
write(annot.element_value_pairs[i], true);
|
||||
for (var element : annot.elements()) {
|
||||
write(element, true);
|
||||
println();
|
||||
}
|
||||
if (showParens) {
|
||||
@ -85,143 +82,126 @@ public class AnnotationWriter extends BasicWriter {
|
||||
}
|
||||
} else {
|
||||
print("(");
|
||||
for (int i = 0; i < annot.num_element_value_pairs; i++) {
|
||||
for (int i = 0; i < annot.elements().size(); i++) {
|
||||
if (i > 0)
|
||||
print(",");
|
||||
write(annot.element_value_pairs[i], false);
|
||||
write(annot.elements().get(i), false);
|
||||
}
|
||||
print(")");
|
||||
}
|
||||
}
|
||||
|
||||
public void write(TypeAnnotation annot) {
|
||||
write(annot, true, false);
|
||||
public void write(TypeAnnotation annot, CodeAttribute lr) {
|
||||
write(annot, true, false, lr);
|
||||
println();
|
||||
indent(+1);
|
||||
write(annot.annotation, true);
|
||||
write(annot, true);
|
||||
indent(-1);
|
||||
}
|
||||
|
||||
public void write(TypeAnnotation annot, boolean showOffsets, boolean resolveIndices) {
|
||||
write(annot.annotation, resolveIndices);
|
||||
public void write(TypeAnnotation annot, boolean showOffsets,
|
||||
boolean resolveIndices, CodeAttribute lr) {
|
||||
write(annot, resolveIndices);
|
||||
print(": ");
|
||||
write(annot.position, showOffsets);
|
||||
write(annot.targetInfo(), annot.targetPath(), showOffsets, lr);
|
||||
}
|
||||
|
||||
public void write(TypeAnnotation.Position pos, boolean showOffsets) {
|
||||
print(pos.type);
|
||||
public void write(TypeAnnotation.TargetInfo targetInfo,
|
||||
List<TypeAnnotation.TypePathComponent> targetPath,
|
||||
boolean showOffsets, CodeAttribute lr) {
|
||||
print(targetInfo.targetType());
|
||||
|
||||
switch (pos.type) {
|
||||
// instanceof
|
||||
case INSTANCEOF:
|
||||
// new expression
|
||||
case NEW:
|
||||
// constructor/method reference receiver
|
||||
case CONSTRUCTOR_REFERENCE:
|
||||
case METHOD_REFERENCE:
|
||||
if (showOffsets) {
|
||||
print(", offset=");
|
||||
print(pos.offset);
|
||||
}
|
||||
break;
|
||||
// local variable
|
||||
case LOCAL_VARIABLE:
|
||||
// resource variable
|
||||
case RESOURCE_VARIABLE:
|
||||
if (pos.lvarOffset == null) {
|
||||
print(", lvarOffset is Null!");
|
||||
break;
|
||||
}
|
||||
print(", {");
|
||||
for (int i = 0; i < pos.lvarOffset.length; ++i) {
|
||||
if (i != 0) print("; ");
|
||||
switch (targetInfo) {
|
||||
// instanceof
|
||||
// new expression
|
||||
// constructor/method reference receiver
|
||||
case TypeAnnotation.OffsetTarget pos -> {
|
||||
if (showOffsets) {
|
||||
print("start_pc=");
|
||||
print(pos.lvarOffset[i]);
|
||||
print(", offset=");
|
||||
print(lr.labelToBci(pos.target()));
|
||||
}
|
||||
print(", length=");
|
||||
print(pos.lvarLength[i]);
|
||||
print(", index=");
|
||||
print(pos.lvarIndex[i]);
|
||||
}
|
||||
print("}");
|
||||
break;
|
||||
// exception parameter
|
||||
case EXCEPTION_PARAMETER:
|
||||
print(", exception_index=");
|
||||
print(pos.exception_index);
|
||||
break;
|
||||
// method receiver
|
||||
case METHOD_RECEIVER:
|
||||
// Do nothing
|
||||
break;
|
||||
// type parameter
|
||||
case CLASS_TYPE_PARAMETER:
|
||||
case METHOD_TYPE_PARAMETER:
|
||||
print(", param_index=");
|
||||
print(pos.parameter_index);
|
||||
break;
|
||||
// type parameter bound
|
||||
case CLASS_TYPE_PARAMETER_BOUND:
|
||||
case METHOD_TYPE_PARAMETER_BOUND:
|
||||
print(", param_index=");
|
||||
print(pos.parameter_index);
|
||||
print(", bound_index=");
|
||||
print(pos.bound_index);
|
||||
break;
|
||||
// class extends or implements clause
|
||||
case CLASS_EXTENDS:
|
||||
print(", type_index=");
|
||||
print(pos.type_index);
|
||||
break;
|
||||
// throws
|
||||
case THROWS:
|
||||
print(", type_index=");
|
||||
print(pos.type_index);
|
||||
break;
|
||||
// method parameter
|
||||
case METHOD_FORMAL_PARAMETER:
|
||||
print(", param_index=");
|
||||
print(pos.parameter_index);
|
||||
break;
|
||||
// type cast
|
||||
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) {
|
||||
print(", offset=");
|
||||
print(pos.offset);
|
||||
case TypeAnnotation.LocalVarTarget pos -> {
|
||||
if (pos.table().isEmpty()) {
|
||||
print(", lvarOffset is Null!");
|
||||
break;
|
||||
}
|
||||
print(", {");
|
||||
var table = pos.table();
|
||||
for (int i = 0; i < table.size(); ++i) {
|
||||
var e = table.get(i);
|
||||
if (i != 0) print("; ");
|
||||
int startPc = lr.labelToBci(e.startLabel());
|
||||
if (showOffsets) {
|
||||
print("start_pc=");
|
||||
print(startPc);
|
||||
}
|
||||
print(", length=");
|
||||
print(lr.labelToBci(e.endLabel()) - startPc);
|
||||
print(", index=");
|
||||
print(e.index());
|
||||
}
|
||||
print("}");
|
||||
}
|
||||
print(", type_index=");
|
||||
print(pos.type_index);
|
||||
break;
|
||||
// We don't need to worry about these
|
||||
case METHOD_RETURN:
|
||||
case FIELD:
|
||||
break;
|
||||
case UNKNOWN:
|
||||
throw new AssertionError("AnnotationWriter: UNKNOWN target type should never occur!");
|
||||
default:
|
||||
throw new AssertionError("AnnotationWriter: Unknown target type for position: " + pos);
|
||||
case TypeAnnotation.CatchTarget pos -> {
|
||||
print(", exception_index=");
|
||||
print(pos.exceptionTableIndex());
|
||||
}
|
||||
case TypeAnnotation.TypeParameterTarget pos -> {
|
||||
print(", param_index=");
|
||||
print(pos.typeParameterIndex());
|
||||
}
|
||||
case TypeAnnotation.TypeParameterBoundTarget pos -> {
|
||||
print(", param_index=");
|
||||
print(pos.typeParameterIndex());
|
||||
print(", bound_index=");
|
||||
print(pos.boundIndex());
|
||||
}
|
||||
case TypeAnnotation.SupertypeTarget pos -> {
|
||||
print(", type_index=");
|
||||
print(pos.supertypeIndex());
|
||||
}
|
||||
case TypeAnnotation.ThrowsTarget pos -> {
|
||||
print(", type_index=");
|
||||
print(pos.throwsTargetIndex());
|
||||
}
|
||||
case TypeAnnotation.FormalParameterTarget pos -> {
|
||||
print(", param_index=");
|
||||
print(pos.formalParameterIndex());
|
||||
}
|
||||
case TypeAnnotation.TypeArgumentTarget pos -> {
|
||||
if (showOffsets) {
|
||||
print(", offset=");
|
||||
print(lr.labelToBci(pos.target()));
|
||||
}
|
||||
print(", type_index=");
|
||||
print(pos.typeArgumentIndex());
|
||||
}
|
||||
case TypeAnnotation.EmptyTarget pos -> {
|
||||
// Do nothing
|
||||
}
|
||||
default ->
|
||||
throw new AssertionError("AnnotationWriter: Unhandled target type: "
|
||||
+ targetInfo.getClass());
|
||||
}
|
||||
|
||||
// Append location data for generics/arrays.
|
||||
if (!pos.location.isEmpty()) {
|
||||
if (!targetPath.isEmpty()) {
|
||||
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) {
|
||||
writeIndex(pair.element_name_index, resolveIndices);
|
||||
public void write(AnnotationElement pair, boolean resolveIndices) {
|
||||
writeIndex(pair.name(), resolveIndices);
|
||||
print("=");
|
||||
write(pair.value, resolveIndices);
|
||||
write(pair.value(), resolveIndices);
|
||||
}
|
||||
|
||||
public void write(Annotation.element_value value) {
|
||||
public void write(AnnotationValue value) {
|
||||
write(value, false);
|
||||
println();
|
||||
indent(+1);
|
||||
@ -229,122 +209,94 @@ public class AnnotationWriter extends BasicWriter {
|
||||
indent(-1);
|
||||
}
|
||||
|
||||
public void write(Annotation.element_value value, boolean resolveIndices) {
|
||||
ev_writer.write(value, resolveIndices);
|
||||
}
|
||||
|
||||
private void writeDescriptor(int index, boolean resolveIndices) {
|
||||
private void writeDescriptor(Utf8Entry entry, boolean resolveIndices) {
|
||||
if (resolveIndices) {
|
||||
try {
|
||||
ConstantPool constant_pool = classWriter.getClassFile().constant_pool;
|
||||
Descriptor d = new Descriptor(index);
|
||||
print(d.getFieldType(constant_pool));
|
||||
return;
|
||||
} catch (ConstantPoolException | InvalidDescriptor ignore) {
|
||||
}
|
||||
print(classWriter.sigPrinter.print(Signature.parseFrom(entry.stringValue())));
|
||||
return;
|
||||
}
|
||||
|
||||
print("#" + index);
|
||||
print("#" + entry.index());
|
||||
}
|
||||
|
||||
private void writeIndex(int index, boolean resolveIndices) {
|
||||
private void writeIndex(PoolEntry entry, boolean resolveIndices) {
|
||||
if (resolveIndices) {
|
||||
print(constantWriter.stringValue(index));
|
||||
print(constantWriter.stringValue(entry));
|
||||
} else
|
||||
print("#" + index);
|
||||
print("#" + entry.index());
|
||||
}
|
||||
|
||||
element_value_Writer ev_writer = new element_value_Writer();
|
||||
|
||||
class element_value_Writer implements Annotation.element_value.Visitor<Void,Boolean> {
|
||||
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) {
|
||||
int index = ev.const_value_index;
|
||||
switch (ev.tag) {
|
||||
case 'B':
|
||||
print("(byte) ");
|
||||
print(constantWriter.stringValue(index));
|
||||
break;
|
||||
case 'C':
|
||||
print("'");
|
||||
print(constantWriter.charValue(index));
|
||||
print("'");
|
||||
break;
|
||||
case 'D':
|
||||
case 'F':
|
||||
case 'I':
|
||||
case 'J':
|
||||
print(constantWriter.stringValue(index));
|
||||
break;
|
||||
case 'S':
|
||||
print("(short) ");
|
||||
print(constantWriter.stringValue(index));
|
||||
break;
|
||||
case 'Z':
|
||||
print(constantWriter.booleanValue(index));
|
||||
break;
|
||||
case 's':
|
||||
print("\"");
|
||||
print(constantWriter.stringValue(index));
|
||||
print("\"");
|
||||
break;
|
||||
default:
|
||||
print(((char) ev.tag) + "#" + ev.const_value_index);
|
||||
break;
|
||||
public void write(AnnotationValue value, boolean resolveIndices) {
|
||||
switch (value) {
|
||||
case AnnotationValue.OfConstant ev -> {
|
||||
if (resolveIndices) {
|
||||
var entry = ev.constant();
|
||||
switch (ev.tag()) {
|
||||
case 'B':
|
||||
print("(byte) ");
|
||||
print(constantWriter.stringValue(entry));
|
||||
break;
|
||||
case 'C':
|
||||
print("'");
|
||||
print(constantWriter.charValue(entry));
|
||||
print("'");
|
||||
break;
|
||||
case 'D':
|
||||
case 'F':
|
||||
case 'I':
|
||||
case 'J':
|
||||
print(constantWriter.stringValue(entry));
|
||||
break;
|
||||
case 'S':
|
||||
print("(short) ");
|
||||
print(constantWriter.stringValue(entry));
|
||||
break;
|
||||
case 'Z':
|
||||
print(constantWriter.booleanValue(entry));
|
||||
break;
|
||||
case 's':
|
||||
print("\"");
|
||||
print(constantWriter.stringValue(entry));
|
||||
print("\"");
|
||||
break;
|
||||
default:
|
||||
print(ev.tag() + "#" + entry.index());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
print(ev.tag() + "#" + ev.constant().index());
|
||||
}
|
||||
} else {
|
||||
print(((char) ev.tag) + "#" + ev.const_value_index);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitEnum(Enum_element_value ev, Boolean resolveIndices) {
|
||||
if (resolveIndices) {
|
||||
writeIndex(ev.type_name_index, resolveIndices);
|
||||
print(".");
|
||||
writeIndex(ev.const_name_index, resolveIndices);
|
||||
} else {
|
||||
print(((char) ev.tag) + "#" + ev.type_name_index + ".#" + ev.const_name_index);
|
||||
case AnnotationValue.OfEnum ev -> {
|
||||
if (resolveIndices) {
|
||||
writeIndex(ev.className(), resolveIndices);
|
||||
print(".");
|
||||
writeIndex(ev.constantName(), resolveIndices);
|
||||
} else {
|
||||
print(ev.tag() + "#" + ev.className().index() + ".#"
|
||||
+ ev.constantName().index());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitClass(Class_element_value ev, Boolean resolveIndices) {
|
||||
if (resolveIndices) {
|
||||
print("class ");
|
||||
writeIndex(ev.class_info_index, resolveIndices);
|
||||
} else {
|
||||
print(((char) ev.tag) + "#" + ev.class_info_index);
|
||||
case AnnotationValue.OfClass ev -> {
|
||||
if (resolveIndices) {
|
||||
print("class ");
|
||||
writeIndex(ev.className(), resolveIndices);
|
||||
} else {
|
||||
print(ev.tag() + "#" + ev.className().index());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitAnnotation(Annotation_element_value ev, Boolean resolveIndices) {
|
||||
print((char) ev.tag);
|
||||
AnnotationWriter.this.write(ev.annotation_value, resolveIndices);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitArray(Array_element_value ev, Boolean resolveIndices) {
|
||||
print("[");
|
||||
for (int i = 0; i < ev.num_values; i++) {
|
||||
if (i > 0)
|
||||
print(",");
|
||||
write(ev.values[i], resolveIndices);
|
||||
case AnnotationValue.OfAnnotation ev -> {
|
||||
print(ev.tag());
|
||||
AnnotationWriter.this.write(ev.annotation(), resolveIndices);
|
||||
}
|
||||
case AnnotationValue.OfArray ev -> {
|
||||
print("[");
|
||||
for (int i = 0; i < ev.values().size(); i++) {
|
||||
if (i > 0)
|
||||
print(",");
|
||||
write(ev.values().get(i), resolveIndices);
|
||||
}
|
||||
print("]");
|
||||
}
|
||||
print("]");
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final ClassWriter classWriter;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -26,10 +26,7 @@
|
||||
package com.sun.tools.javap;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import com.sun.tools.classfile.AttributeException;
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
import com.sun.tools.classfile.DescriptorException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/*
|
||||
* 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());
|
||||
}
|
||||
|
||||
protected void print(Supplier<Object> safeguardedCode) {
|
||||
try {
|
||||
print(safeguardedCode.get());
|
||||
} catch (IllegalArgumentException e) {
|
||||
print(report(e));
|
||||
}
|
||||
}
|
||||
|
||||
protected void println() {
|
||||
lineWriter.println();
|
||||
}
|
||||
@ -71,6 +76,11 @@ public class BasicWriter {
|
||||
lineWriter.println();
|
||||
}
|
||||
|
||||
protected void println(Supplier<Object> safeguardedCode) {
|
||||
print(safeguardedCode);
|
||||
lineWriter.println();
|
||||
}
|
||||
|
||||
protected void indent(int delta) {
|
||||
lineWriter.indent(delta);
|
||||
}
|
||||
@ -83,23 +93,15 @@ public class BasicWriter {
|
||||
lineWriter.pendingNewline = b;
|
||||
}
|
||||
|
||||
protected String report(AttributeException 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) {
|
||||
protected String report(Exception e) {
|
||||
out.println("Error: " + e.getMessage()); // i18n?
|
||||
errorReported = true;
|
||||
return "???";
|
||||
}
|
||||
|
||||
protected String report(String msg) {
|
||||
out.println("Error: " + msg); // i18n?
|
||||
errorReported = true;
|
||||
return "???";
|
||||
}
|
||||
|
||||
@ -123,6 +125,7 @@ public class BasicWriter {
|
||||
private LineWriter lineWriter;
|
||||
private PrintWriter out;
|
||||
protected Messages messages;
|
||||
protected boolean errorReported;
|
||||
|
||||
private static class LineWriter {
|
||||
static LineWriter instance(Context context) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,14 +28,15 @@ package com.sun.tools.javap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sun.tools.classfile.AccessFlags;
|
||||
import com.sun.tools.classfile.Code_attribute;
|
||||
import com.sun.tools.classfile.ConstantPool;
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
import com.sun.tools.classfile.DescriptorException;
|
||||
import com.sun.tools.classfile.Instruction;
|
||||
import com.sun.tools.classfile.Instruction.TypeKind;
|
||||
import com.sun.tools.classfile.Method;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.Opcode;
|
||||
import jdk.internal.classfile.constantpool.*;
|
||||
import jdk.internal.classfile.Instruction;
|
||||
import jdk.internal.classfile.MethodModel;
|
||||
import jdk.internal.classfile.attribute.CodeAttribute;
|
||||
import jdk.internal.classfile.instruction.*;
|
||||
|
||||
/*
|
||||
* Write the contents of a Code attribute.
|
||||
@ -68,161 +69,148 @@ public class CodeWriter extends BasicWriter {
|
||||
options = Options.instance(context);
|
||||
}
|
||||
|
||||
void write(Code_attribute attr, ConstantPool constant_pool) {
|
||||
void write(CodeAttribute attr) {
|
||||
println("Code:");
|
||||
indent(+1);
|
||||
writeVerboseHeader(attr, constant_pool);
|
||||
writeVerboseHeader(attr);
|
||||
writeInstrs(attr);
|
||||
writeExceptionTable(attr);
|
||||
attrWriter.write(attr, attr.attributes, constant_pool);
|
||||
attrWriter.write(attr.attributes(), attr);
|
||||
indent(-1);
|
||||
}
|
||||
|
||||
public void writeVerboseHeader(Code_attribute attr, ConstantPool constant_pool) {
|
||||
Method method = classWriter.getMethod();
|
||||
String argCount;
|
||||
try {
|
||||
int n = method.descriptor.getParameterCount(constant_pool);
|
||||
if (!method.access_flags.is(AccessFlags.ACC_STATIC))
|
||||
++n; // for 'this'
|
||||
argCount = Integer.toString(n);
|
||||
} catch (ConstantPoolException e) {
|
||||
argCount = report(e);
|
||||
} catch (DescriptorException e) {
|
||||
argCount = report(e);
|
||||
}
|
||||
|
||||
println("stack=" + attr.max_stack +
|
||||
", locals=" + attr.max_locals +
|
||||
", args_size=" + argCount);
|
||||
|
||||
public void writeVerboseHeader(CodeAttribute attr) {
|
||||
MethodModel method = attr.parent().get();
|
||||
int n = method.methodTypeSymbol().parameterCount();
|
||||
if ((method.flags().flagsMask() & Classfile.ACC_STATIC) == 0)
|
||||
++n; // for 'this'
|
||||
println("stack=" + attr.maxStack() +
|
||||
", locals=" + attr.maxLocals() +
|
||||
", args_size=" + Integer.toString(n));
|
||||
}
|
||||
|
||||
public void writeInstrs(Code_attribute attr) {
|
||||
public void writeInstrs(CodeAttribute attr) {
|
||||
List<InstructionDetailWriter> detailWriters = getDetailWriters(attr);
|
||||
|
||||
for (Instruction instr: attr.getInstructions()) {
|
||||
try {
|
||||
for (InstructionDetailWriter w: detailWriters)
|
||||
w.writeDetails(instr);
|
||||
writeInstr(instr);
|
||||
} catch (ArrayIndexOutOfBoundsException | IllegalStateException e) {
|
||||
println(report("error at or after byte " + instr.getPC()));
|
||||
break;
|
||||
int pc = 0;
|
||||
try {
|
||||
for (var coe: attr) {
|
||||
if (coe instanceof Instruction instr) {
|
||||
for (InstructionDetailWriter w: detailWriters)
|
||||
w.writeDetails(pc, instr);
|
||||
writeInstr(pc, instr, attr);
|
||||
pc += instr.sizeInBytes();
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
report("error at or after byte " + pc);
|
||||
}
|
||||
|
||||
for (InstructionDetailWriter w: detailWriters)
|
||||
w.flush();
|
||||
w.flush(pc);
|
||||
}
|
||||
|
||||
public void writeInstr(Instruction instr) {
|
||||
print(String.format("%4d: %-13s ", instr.getPC(), instr.getMnemonic()));
|
||||
// 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,
|
||||
// and rounded up to the next integer.
|
||||
int indentWidth = options.indentWidth;
|
||||
int indent = (6 + indentWidth - 1) / indentWidth;
|
||||
instr.accept(instructionPrinter, indent);
|
||||
println();
|
||||
public void writeInstr(int pc, Instruction ins, CodeAttribute lr) {
|
||||
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
|
||||
// This is 6 (the width of "%4d: "), divided by the width of each indentation level,
|
||||
// and rounded up to the next integer.
|
||||
int indentWidth = options.indentWidth;
|
||||
int indent = (6 + indentWidth - 1) / indentWidth;
|
||||
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();
|
||||
} catch (IllegalArgumentException e) {
|
||||
println(report(e));
|
||||
}
|
||||
}
|
||||
// where
|
||||
Instruction.KindVisitor<Void,Integer> instructionPrinter =
|
||||
new Instruction.KindVisitor<>() {
|
||||
|
||||
public Void visitNoOperands(Instruction instr, Integer indent) {
|
||||
return null;
|
||||
}
|
||||
private void printConstantPoolRef(PoolEntry entry) {
|
||||
print("#" + entry.index());
|
||||
tab();
|
||||
print("// ");
|
||||
constantWriter.write(entry.index());
|
||||
}
|
||||
|
||||
public Void visitArrayType(Instruction instr, TypeKind kind, Integer indent) {
|
||||
print(" " + kind.name);
|
||||
return null;
|
||||
}
|
||||
private void printConstantPoolRefAndValue(PoolEntry entry, int value) {
|
||||
print("#" + entry.index() + ", " + value);
|
||||
tab();
|
||||
print("// ");
|
||||
constantWriter.write(entry.index());
|
||||
}
|
||||
|
||||
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();
|
||||
print("// ");
|
||||
printConstant(index);
|
||||
return null;
|
||||
}
|
||||
|
||||
public Void visitConstantPoolRefAndValue(Instruction instr, int index, int value, Integer indent) {
|
||||
print("#" + index + ", " + value);
|
||||
tab();
|
||||
print("// ");
|
||||
printConstant(index);
|
||||
return null;
|
||||
}
|
||||
|
||||
public Void visitLocal(Instruction instr, int index, Integer indent) {
|
||||
print(index);
|
||||
return null;
|
||||
}
|
||||
|
||||
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) {
|
||||
public void writeExceptionTable(CodeAttribute attr) {
|
||||
var excTable = attr.exceptionHandlers();
|
||||
if (excTable.size() > 0) {
|
||||
println("Exception table:");
|
||||
indent(+1);
|
||||
println(" from to target type");
|
||||
for (int i = 0; i < attr.exception_table.length; i++) {
|
||||
Code_attribute.Exception_data handler = attr.exception_table[i];
|
||||
for (var handler : excTable) {
|
||||
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(" ");
|
||||
int catch_type = handler.catch_type;
|
||||
if (catch_type == 0) {
|
||||
var catch_type = handler.catchType();
|
||||
if (catch_type.isEmpty()) {
|
||||
println("any");
|
||||
} else {
|
||||
print("Class ");
|
||||
println(constantWriter.stringValue(catch_type));
|
||||
println(constantWriter.stringValue(catch_type.get()));
|
||||
}
|
||||
}
|
||||
indent(-1);
|
||||
@ -230,14 +218,10 @@ public class CodeWriter extends BasicWriter {
|
||||
|
||||
}
|
||||
|
||||
private void printConstant(int index) {
|
||||
constantWriter.write(index);
|
||||
}
|
||||
|
||||
private List<InstructionDetailWriter> getDetailWriters(Code_attribute attr) {
|
||||
private List<InstructionDetailWriter> getDetailWriters(CodeAttribute attr) {
|
||||
List<InstructionDetailWriter> detailWriters = new ArrayList<>();
|
||||
if (options.details.contains(InstructionDetailWriter.Kind.SOURCE)) {
|
||||
sourceWriter.reset(classWriter.getClassFile(), attr);
|
||||
sourceWriter.reset(attr);
|
||||
if (sourceWriter.hasSource())
|
||||
detailWriters.add(sourceWriter);
|
||||
else
|
||||
|
@ -25,11 +25,8 @@
|
||||
|
||||
package com.sun.tools.javap;
|
||||
|
||||
import com.sun.tools.classfile.ClassFile;
|
||||
import com.sun.tools.classfile.ConstantPool;
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
|
||||
import static com.sun.tools.classfile.ConstantPool.*;
|
||||
import jdk.internal.classfile.constantpool.*;
|
||||
import static jdk.internal.classfile.Classfile.*;
|
||||
|
||||
/*
|
||||
* Write a constant pool entry.
|
||||
@ -55,122 +52,11 @@ public class ConstantWriter extends BasicWriter {
|
||||
}
|
||||
|
||||
protected void writeConstantPool() {
|
||||
ConstantPool constant_pool = classWriter.getClassFile().constant_pool;
|
||||
var constant_pool = classWriter.getClassModel().constantPool();
|
||||
writeConstantPool(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:");
|
||||
indent(+1);
|
||||
int width = String.valueOf(constant_pool.size()).length() + 1;
|
||||
@ -178,321 +64,228 @@ public class ConstantWriter extends BasicWriter {
|
||||
while (cpx < constant_pool.size()) {
|
||||
print(String.format("%" + width + "s", ("#" + cpx)));
|
||||
try {
|
||||
CPInfo cpInfo = constant_pool.get(cpx);
|
||||
print(String.format(" = %-18s ", cpTagName(cpInfo)));
|
||||
cpx += cpInfo.accept(v, null);
|
||||
} catch (ConstantPool.InvalidIndex ex) {
|
||||
// should not happen
|
||||
var cpInfo = constant_pool.entryByIndex(cpx);
|
||||
print(String.format(" = %-18s ", cpTagName(cpInfo.tag())));
|
||||
switch (cpInfo) {
|
||||
case ClassEntry info -> {
|
||||
print(() -> "#" + info.name().index());
|
||||
tab();
|
||||
println(() -> "// " + stringValue(info));
|
||||
}
|
||||
case AnnotationConstantValueEntry info -> {
|
||||
println(() -> stringValue(info));
|
||||
}
|
||||
case MemberRefEntry info -> {
|
||||
print(() -> "#" + info.owner().index() + ".#"
|
||||
+ info.nameAndType().index());
|
||||
tab();
|
||||
println(() -> "// " + stringValue(info));
|
||||
}
|
||||
case DynamicConstantPoolEntry info -> {
|
||||
print(() -> "#" + info.bootstrapMethodIndex() + ":#"
|
||||
+ info.nameAndType().index());
|
||||
tab();
|
||||
println(() -> "// " + stringValue(info));
|
||||
}
|
||||
case MethodHandleEntry info -> {
|
||||
print(() -> info.kind() + ":#" + info.reference().index());
|
||||
tab();
|
||||
println(() -> "// " + stringValue(info));
|
||||
}
|
||||
case MethodTypeEntry info -> {
|
||||
print(() -> "#" + info.descriptor().index());
|
||||
tab();
|
||||
println(() -> "// " + stringValue(info));
|
||||
}
|
||||
case ModuleEntry info -> {
|
||||
print(() -> "#" + info.name().index());
|
||||
tab();
|
||||
println(() -> "// " + stringValue(info));
|
||||
}
|
||||
case NameAndTypeEntry info -> {
|
||||
print(() -> "#" + info.name().index() + ":#" + info.type().index());
|
||||
tab();
|
||||
println(() -> "// " + stringValue(info));
|
||||
}
|
||||
case PackageEntry info -> {
|
||||
print(() -> "#" + info.name().index());
|
||||
tab();
|
||||
println("// " + stringValue(info));
|
||||
}
|
||||
case StringEntry info -> {
|
||||
print(() -> "#" + info.utf8().index());
|
||||
tab();
|
||||
println(() -> "// " + stringValue(info));
|
||||
}
|
||||
default ->
|
||||
throw new IllegalArgumentException("unknown entry: "+ cpInfo);
|
||||
}
|
||||
cpx += cpInfo.width();
|
||||
} catch (IllegalArgumentException e) {
|
||||
println(report(e));
|
||||
cpx++;
|
||||
}
|
||||
}
|
||||
indent(-1);
|
||||
}
|
||||
|
||||
protected void write(int cpx) {
|
||||
ClassFile classFile = classWriter.getClassFile();
|
||||
if (cpx == 0) {
|
||||
print("#0");
|
||||
return;
|
||||
}
|
||||
var classModel = classWriter.getClassModel();
|
||||
|
||||
CPInfo cpInfo;
|
||||
try {
|
||||
cpInfo = classFile.constant_pool.get(cpx);
|
||||
} 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
|
||||
CPRefInfo ref = (CPRefInfo) cpInfo;
|
||||
try {
|
||||
if (ref.class_index == classFile.this_class)
|
||||
cpInfo = classFile.constant_pool.get(ref.name_and_type_index);
|
||||
} catch (ConstantPool.InvalidIndex e) {
|
||||
// ignore, for now
|
||||
}
|
||||
var cpInfo = classModel.constantPool().entryByIndex(cpx);
|
||||
var tag = cpInfo.tag();
|
||||
if (cpInfo instanceof MemberRefEntry ref) {
|
||||
// simplify references within this class
|
||||
if (ref.owner().index() == classModel.thisClass().index())
|
||||
cpInfo = ref.nameAndType();
|
||||
}
|
||||
print(tagName(tag) + " " + stringValue(cpInfo));
|
||||
}
|
||||
|
||||
String cpTagName(CPInfo cpInfo) {
|
||||
String n = cpInfo.getClass().getSimpleName();
|
||||
return n.replace("CONSTANT_", "").replace("_info", "");
|
||||
String cpTagName(int tag) {
|
||||
return switch (tag) {
|
||||
case TAG_UTF8 -> "Utf8";
|
||||
case TAG_INTEGER -> "Integer";
|
||||
case TAG_FLOAT -> "Float";
|
||||
case TAG_LONG -> "Long";
|
||||
case TAG_DOUBLE -> "Double";
|
||||
case TAG_CLASS -> "Class";
|
||||
case TAG_STRING -> "String";
|
||||
case TAG_FIELDREF -> "Fieldref";
|
||||
case TAG_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) {
|
||||
switch (tag) {
|
||||
case CONSTANT_Utf8:
|
||||
return "Utf8";
|
||||
case CONSTANT_Integer:
|
||||
return "int";
|
||||
case CONSTANT_Float:
|
||||
return "float";
|
||||
case CONSTANT_Long:
|
||||
return "long";
|
||||
case CONSTANT_Double:
|
||||
return "double";
|
||||
case CONSTANT_Class:
|
||||
return "class";
|
||||
case CONSTANT_String:
|
||||
return "String";
|
||||
case CONSTANT_Fieldref:
|
||||
return "Field";
|
||||
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 + ")";
|
||||
return switch (tag) {
|
||||
case TAG_UTF8 -> "Utf8";
|
||||
case TAG_INTEGER -> "int";
|
||||
case TAG_FLOAT -> "float";
|
||||
case TAG_LONG -> "long";
|
||||
case TAG_DOUBLE -> "double";
|
||||
case TAG_CLASS -> "class";
|
||||
case TAG_STRING -> "String";
|
||||
case TAG_FIELDREF -> "Field";
|
||||
case TAG_METHODHANDLE -> "MethodHandle";
|
||||
case TAG_METHODTYPE -> "MethodType";
|
||||
case TAG_METHODREF -> "Method";
|
||||
case TAG_INTERFACEMETHODREF -> "InterfaceMethod";
|
||||
case TAG_INVOKEDYNAMIC -> "InvokeDynamic";
|
||||
case TAG_CONSTANTDYNAMIC -> "Dynamic";
|
||||
case TAG_NAMEANDTYPE -> "NameAndType";
|
||||
default -> "(unknown tag " + tag + ")";
|
||||
};
|
||||
}
|
||||
|
||||
String booleanValue(PoolEntry info) {
|
||||
if (info instanceof IntegerEntry ie) {
|
||||
switch (ie.intValue()) {
|
||||
case 0: return "false";
|
||||
case 1: return "true";
|
||||
}
|
||||
}
|
||||
return "#" + info.index();
|
||||
}
|
||||
|
||||
String booleanValue(int constant_pool_index) {
|
||||
ClassFile classFile = classWriter.getClassFile();
|
||||
try {
|
||||
CPInfo info = classFile.constant_pool.get(constant_pool_index);
|
||||
if (info instanceof CONSTANT_Integer_info) {
|
||||
int value = ((CONSTANT_Integer_info) info).value;
|
||||
switch (value) {
|
||||
case 0: return "false";
|
||||
case 1: return "true";
|
||||
}
|
||||
}
|
||||
return "#" + constant_pool_index;
|
||||
} catch (ConstantPool.InvalidIndex e) {
|
||||
return report(e);
|
||||
var info = classWriter.getClassModel().constantPool()
|
||||
.entryByIndex(constant_pool_index);
|
||||
if (info instanceof IntegerEntry ie) {
|
||||
switch (ie.intValue()) {
|
||||
case 0: return "false";
|
||||
case 1: return "true";
|
||||
}
|
||||
}
|
||||
return "#" + constant_pool_index;
|
||||
}
|
||||
|
||||
String charValue(PoolEntry info) {
|
||||
if (info instanceof IntegerEntry ie) {
|
||||
int value = ie.intValue();
|
||||
return String.valueOf((char) value);
|
||||
} else {
|
||||
return "#" + info.index();
|
||||
}
|
||||
}
|
||||
|
||||
String charValue(int constant_pool_index) {
|
||||
ClassFile classFile = classWriter.getClassFile();
|
||||
try {
|
||||
CPInfo info = classFile.constant_pool.get(constant_pool_index);
|
||||
if (info instanceof CONSTANT_Integer_info) {
|
||||
int value = ((CONSTANT_Integer_info) info).value;
|
||||
return String.valueOf((char) value);
|
||||
} else {
|
||||
return "#" + constant_pool_index;
|
||||
}
|
||||
} catch (ConstantPool.InvalidIndex e) {
|
||||
return report(e);
|
||||
var info = classWriter.getClassModel().constantPool()
|
||||
.entryByIndex(constant_pool_index);
|
||||
if (info instanceof IntegerEntry ie) {
|
||||
int value = ie.intValue();
|
||||
return String.valueOf((char) value);
|
||||
} else {
|
||||
return "#" + constant_pool_index;
|
||||
}
|
||||
}
|
||||
|
||||
String stringValue(int constant_pool_index) {
|
||||
ClassFile classFile = classWriter.getClassFile();
|
||||
try {
|
||||
return stringValue(classFile.constant_pool.get(constant_pool_index));
|
||||
} catch (ConstantPool.InvalidIndex e) {
|
||||
return report(e);
|
||||
}
|
||||
return stringValue(classWriter.getClassModel().constantPool()
|
||||
.entryByIndex(constant_pool_index));
|
||||
}
|
||||
|
||||
String stringValue(CPInfo cpInfo) {
|
||||
return stringValueVisitor.visit(cpInfo);
|
||||
}
|
||||
|
||||
StringValueVisitor stringValueVisitor = new StringValueVisitor();
|
||||
|
||||
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);
|
||||
String stringValue(PoolEntry cpInfo) {
|
||||
return switch (cpInfo) {
|
||||
case ClassEntry info -> checkName(info.asInternalName());
|
||||
case DoubleEntry info -> info.doubleValue() + "d";
|
||||
case MemberRefEntry info -> checkName(info.owner().asInternalName())
|
||||
+ '.' + stringValue(info.nameAndType());
|
||||
case FloatEntry info -> info.floatValue()+ "f";
|
||||
case IntegerEntry info -> String.valueOf(info.intValue());
|
||||
case DynamicConstantPoolEntry info -> "#" + info.bootstrapMethodIndex()
|
||||
+ ":" + stringValue(info.nameAndType());
|
||||
case LongEntry info -> info.longValue()+ "l";
|
||||
case ModuleEntry info -> checkName(info.name().stringValue());
|
||||
case NameAndTypeEntry info -> checkName(info.name().stringValue())
|
||||
+ ':' + info.type().stringValue();
|
||||
case PackageEntry info -> checkName(info.name().stringValue());
|
||||
case MethodHandleEntry info -> {
|
||||
String kind = switch (info.asSymbol().kind()) {
|
||||
case STATIC, INTERFACE_STATIC -> "REF_invokeStatic";
|
||||
case VIRTUAL -> "REF_invokeVirtual";
|
||||
case INTERFACE_VIRTUAL -> "REF_invokeInterface";
|
||||
case SPECIAL, INTERFACE_SPECIAL -> "REF_invokeSpecial";
|
||||
case CONSTRUCTOR -> "REF_newInvokeSpecial";
|
||||
case GETTER -> "REF_getField";
|
||||
case SETTER -> "REF_putField";
|
||||
case STATIC_GETTER -> "REF_getStatic";
|
||||
case STATIC_SETTER -> "REF_putStatic";
|
||||
};
|
||||
yield kind + " " + stringValue(info.reference());
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
switch (c) {
|
||||
case '\t':
|
||||
sb.append('\\').append('t');
|
||||
break;
|
||||
case '\n':
|
||||
sb.append('\\').append('n');
|
||||
break;
|
||||
case '\r':
|
||||
sb.append('\\').append('r');
|
||||
break;
|
||||
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);
|
||||
case MethodTypeEntry info -> info.descriptor().stringValue();
|
||||
case StringEntry info -> stringValue(info.utf8());
|
||||
case Utf8Entry info -> {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (char c : info.stringValue().toCharArray()) {
|
||||
sb.append(switch (c) {
|
||||
case '\t' -> "\\t";
|
||||
case '\n' -> "\\n";
|
||||
case '\r' -> "\\r";
|
||||
case '\b' -> "\\b";
|
||||
case '\f' -> "\\f";
|
||||
case '\"' -> "\\\"";
|
||||
case '\'' -> "\\\'";
|
||||
case '\\' -> "\\\\";
|
||||
default -> Character.isISOControl(c)
|
||||
? String.format("\\u%04x", (int) c) : c;
|
||||
});
|
||||
}
|
||||
yield sb.toString();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
default -> throw new IllegalArgumentException("unknown " + cpInfo);
|
||||
};
|
||||
}
|
||||
|
||||
/* If name is a valid binary name, return it; otherwise quote it. */
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
abstract void writeDetails(Instruction instr);
|
||||
void flush() { }
|
||||
abstract void writeDetails(int pc, Instruction instr);
|
||||
void flush(int pc) { }
|
||||
}
|
||||
|
@ -67,7 +67,10 @@ import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
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
|
||||
@ -166,7 +169,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
@Override
|
||||
void process(JavapTask task, String opt, String arg) {
|
||||
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
|
||||
void process(JavapTask task, String opt, String arg) {
|
||||
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.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.put(Messages.class, this);
|
||||
options = Options.instance(context);
|
||||
attributeFactory = new Attribute.Factory();
|
||||
}
|
||||
|
||||
public JavapTask(Writer out,
|
||||
@ -633,7 +635,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
} catch (OutOfMemoryError e) {
|
||||
reportError("err.nomem");
|
||||
result = EXIT_ERROR;
|
||||
} catch (FatalError e) {
|
||||
} catch (IllegalArgumentException e) {
|
||||
Object msg = e.getLocalizedMessage();
|
||||
if (msg == null) {
|
||||
msg = e;
|
||||
@ -654,7 +656,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
}
|
||||
|
||||
protected int writeClass(ClassWriter classWriter, String className)
|
||||
throws IOException, ConstantPoolException {
|
||||
throws IOException {
|
||||
JavaFileObject fo = open(className);
|
||||
if (fo == null) {
|
||||
reportError("err.class.not.found", className);
|
||||
@ -663,34 +665,26 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
|
||||
ClassFileInfo cfInfo = read(fo);
|
||||
if (!className.endsWith(".class")) {
|
||||
if (cfInfo.cf.this_class == 0) {
|
||||
if (!className.equals("module-info")) {
|
||||
reportWarning("warn.unexpected.class", fo.getName(), className);
|
||||
}
|
||||
} else {
|
||||
String cfName = cfInfo.cf.getName();
|
||||
if (!cfName.replaceAll("[/$]", ".").equals(className.replaceAll("[/$]", "."))) {
|
||||
reportWarning("warn.unexpected.class", fo.getName(), className);
|
||||
}
|
||||
String cfName = cfInfo.cm.thisClass().asInternalName();
|
||||
if (!cfName.replaceAll("[/$]", ".").equals(className.replaceAll("[/$]", "."))) {
|
||||
reportWarning("warn.unexpected.class", fo.getName(), className);
|
||||
}
|
||||
}
|
||||
write(cfInfo);
|
||||
if (!write(cfInfo)) return EXIT_ERROR;
|
||||
|
||||
if (options.showInnerClasses) {
|
||||
ClassFile cf = cfInfo.cf;
|
||||
Attribute a = cf.getAttribute(Attribute.InnerClasses);
|
||||
if (a instanceof InnerClasses_attribute) {
|
||||
InnerClasses_attribute inners = (InnerClasses_attribute) a;
|
||||
ClassModel cm = cfInfo.cm;
|
||||
var a = cm.findAttribute(jdk.internal.classfile.Attributes.INNER_CLASSES);
|
||||
if (a.isPresent()) {
|
||||
var inners = a.get();
|
||||
try {
|
||||
int result = EXIT_OK;
|
||||
for (int i = 0; i < inners.classes.length; i++) {
|
||||
int outerIndex = inners.classes[i].outer_class_info_index;
|
||||
ConstantPool.CONSTANT_Class_info outerClassInfo = cf.constant_pool.getClassInfo(outerIndex);
|
||||
String outerClassName = outerClassInfo.getName();
|
||||
if (outerClassName.equals(cf.getName())) {
|
||||
int innerIndex = inners.classes[i].inner_class_info_index;
|
||||
ConstantPool.CONSTANT_Class_info innerClassInfo = cf.constant_pool.getClassInfo(innerIndex);
|
||||
String innerClassName = innerClassInfo.getName();
|
||||
for (var inner : inners.classes()) {
|
||||
var outerClassInfo = inner.outerClass();
|
||||
String outerClassName = outerClassInfo.map(ClassEntry::asInternalName).orElse(null);
|
||||
if (cm.thisClass().asInternalName().equals(outerClassName)) {
|
||||
var innerClassInfo = inner.innerClass();
|
||||
String innerClassName = innerClassInfo.asInternalName();
|
||||
classWriter.println("// inner class " + innerClassName.replaceAll("[/$]", "."));
|
||||
classWriter.println();
|
||||
result = writeClass(classWriter, innerClassName);
|
||||
@ -698,13 +692,10 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (ConstantPoolException e) {
|
||||
} catch (IllegalArgumentException e) {
|
||||
reportError("err.bad.innerclasses.attribute", className);
|
||||
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 {
|
||||
ClassFileInfo(JavaFileObject fo, ClassFile cf, byte[] digest, int size) {
|
||||
ClassFileInfo(JavaFileObject fo, ClassModel cm, byte[] digest, int size) {
|
||||
this.fo = fo;
|
||||
this.cf = cf;
|
||||
this.cm = cm;
|
||||
this.digest = digest;
|
||||
this.size = size;
|
||||
}
|
||||
public final JavaFileObject fo;
|
||||
public final ClassFile cf;
|
||||
public final ClassModel cm;
|
||||
public final byte[] digest;
|
||||
public final int size;
|
||||
}
|
||||
|
||||
public ClassFileInfo read(JavaFileObject fo) throws IOException, ConstantPoolException {
|
||||
public ClassFileInfo read(JavaFileObject fo) throws IOException {
|
||||
InputStream in = fo.openInputStream();
|
||||
try {
|
||||
SizeInputStream sizeIn = null;
|
||||
@ -836,17 +827,16 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
in = new DigestInputStream(in, md);
|
||||
in = sizeIn = new SizeInputStream(in);
|
||||
}
|
||||
|
||||
ClassFile cf = ClassFile.read(in, attributeFactory);
|
||||
ClassModel cm = Classfile.of().parse(in.readAllBytes());
|
||||
byte[] digest = (md == null) ? null : md.digest();
|
||||
int size = (sizeIn == null) ? -1 : sizeIn.size();
|
||||
return new ClassFileInfo(fo, cf, digest, size);
|
||||
return new ClassFileInfo(fo, cm, digest, size);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void write(ClassFileInfo info) {
|
||||
public boolean write(ClassFileInfo info) {
|
||||
ClassWriter classWriter = ClassWriter.instance(context);
|
||||
if (options.sysInfo || options.verbose) {
|
||||
classWriter.setFile(info.fo.toUri());
|
||||
@ -855,56 +845,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
classWriter.setFileSize(info.size);
|
||||
}
|
||||
|
||||
classWriter.write(info.cf);
|
||||
}
|
||||
|
||||
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);
|
||||
return classWriter.write(info.cm);
|
||||
}
|
||||
|
||||
private JavaFileManager getDefaultFileManager(final DiagnosticListener<? super JavaFileObject> dl, PrintWriter log) {
|
||||
@ -944,7 +885,8 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
if (result == null)
|
||||
result = l;
|
||||
else
|
||||
throw new IOException("multiple definitions found for " + moduleName);
|
||||
throw new IOException("multiple definitions found for "
|
||||
+ moduleName);
|
||||
}
|
||||
}
|
||||
if (result != null)
|
||||
@ -957,7 +899,8 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
private void showHelp() {
|
||||
printLines(getMessage("main.usage", progname));
|
||||
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"))
|
||||
continue;
|
||||
printLines(getMessage("main.opt." + name));
|
||||
@ -1000,7 +943,8 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
try {
|
||||
versionRB = ResourceBundle.getBundle(versionRBName);
|
||||
} catch (MissingResourceException e) {
|
||||
return getMessage("version.resource.missing", System.getProperty("java.version"));
|
||||
return getMessage("version.resource.missing",
|
||||
System.getProperty("java.version"));
|
||||
}
|
||||
}
|
||||
try {
|
||||
@ -1064,7 +1008,8 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
|
||||
@Override
|
||||
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);
|
||||
if (b == null) {
|
||||
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);
|
||||
} 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;
|
||||
Locale task_locale;
|
||||
Map<Locale, ResourceBundle> bundles;
|
||||
protected Attribute.Factory attributeFactory;
|
||||
|
||||
private static final String progname = "javap";
|
||||
|
||||
|
@ -25,19 +25,15 @@
|
||||
|
||||
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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
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.
|
||||
@ -47,22 +43,24 @@ import java.util.Map;
|
||||
* This code and its internal interfaces are subject to change or
|
||||
* deletion without notice.</b>
|
||||
*/
|
||||
public class LocalVariableTableWriter extends InstructionDetailWriter {
|
||||
public class LocalVariableTableWriter extends InstructionDetailWriter {
|
||||
public enum NoteKind {
|
||||
START("start") {
|
||||
public boolean match(LocalVariableTable_attribute.Entry entry, int pc) {
|
||||
return (pc == entry.start_pc);
|
||||
@Override
|
||||
public boolean match(LocalVariableInfo entry, int pc) {
|
||||
return (pc == entry.startPc());
|
||||
}
|
||||
},
|
||||
END("end") {
|
||||
public boolean match(LocalVariableTable_attribute.Entry entry, int pc) {
|
||||
return (pc == entry.start_pc + entry.length);
|
||||
@Override
|
||||
public boolean match(LocalVariableInfo entry, int pc) {
|
||||
return (pc == entry.startPc() + entry.length());
|
||||
}
|
||||
};
|
||||
NoteKind(String 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;
|
||||
}
|
||||
|
||||
@ -79,71 +77,56 @@ public class LocalVariableTableWriter extends InstructionDetailWriter {
|
||||
classWriter = ClassWriter.instance(context);
|
||||
}
|
||||
|
||||
public void reset(Code_attribute attr) {
|
||||
public void reset(CodeModel attr) {
|
||||
codeAttr = attr;
|
||||
pcMap = new HashMap<>();
|
||||
LocalVariableTable_attribute lvt =
|
||||
(LocalVariableTable_attribute) (attr.attributes.get(Attribute.LocalVariableTable));
|
||||
if (lvt == null)
|
||||
var lvt = attr.findAttribute(Attributes.LOCAL_VARIABLE_TABLE);
|
||||
|
||||
if (lvt.isEmpty())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < lvt.local_variable_table.length; i++) {
|
||||
LocalVariableTable_attribute.Entry entry = lvt.local_variable_table[i];
|
||||
put(entry.start_pc, entry);
|
||||
put(entry.start_pc + entry.length, entry);
|
||||
for (var entry : lvt.get().localVariables()) {
|
||||
put(entry.startPc(), entry);
|
||||
put(entry.startPc() + entry.length(), entry);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeDetails(Instruction instr) {
|
||||
int pc = instr.getPC();
|
||||
@Override
|
||||
public void writeDetails(int pc, Instruction instr) {
|
||||
writeLocalVariables(pc, NoteKind.END);
|
||||
writeLocalVariables(pc, NoteKind.START);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
int pc = codeAttr.code_length;
|
||||
public void flush(int pc) {
|
||||
writeLocalVariables(pc, NoteKind.END);
|
||||
}
|
||||
|
||||
public void writeLocalVariables(int pc, NoteKind kind) {
|
||||
ConstantPool constant_pool = classWriter.getClassFile().constant_pool;
|
||||
String indent = space(2); // get from Options?
|
||||
List<LocalVariableTable_attribute.Entry> entries = pcMap.get(pc);
|
||||
var entries = pcMap.get(pc);
|
||||
if (entries != null) {
|
||||
for (ListIterator<LocalVariableTable_attribute.Entry> iter =
|
||||
entries.listIterator(kind == NoteKind.END ? entries.size() : 0);
|
||||
for (var iter = entries.listIterator(kind == NoteKind.END ? entries.size() : 0);
|
||||
kind == NoteKind.END ? iter.hasPrevious() : iter.hasNext() ; ) {
|
||||
LocalVariableTable_attribute.Entry entry =
|
||||
kind == NoteKind.END ? iter.previous() : iter.next();
|
||||
var entry = kind == NoteKind.END ? iter.previous() : iter.next();
|
||||
if (kind.match(entry, pc)) {
|
||||
print(indent);
|
||||
print(kind.text);
|
||||
print(" local ");
|
||||
print(entry.index);
|
||||
print(entry.slot());
|
||||
print(" // ");
|
||||
Descriptor d = new Descriptor(entry.descriptor_index);
|
||||
try {
|
||||
print(d.getFieldType(constant_pool));
|
||||
} catch (InvalidDescriptor e) {
|
||||
print(report(e));
|
||||
} catch (ConstantPoolException e) {
|
||||
print(report(e));
|
||||
}
|
||||
print(classWriter.sigPrinter.print(
|
||||
Signature.parseFrom(entry.type().stringValue())));
|
||||
print(" ");
|
||||
try {
|
||||
print(constant_pool.getUTF8Value(entry.name_index));
|
||||
} catch (ConstantPoolException e) {
|
||||
print(report(e));
|
||||
}
|
||||
print(entry.name().stringValue());
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void put(int pc, LocalVariableTable_attribute.Entry entry) {
|
||||
List<LocalVariableTable_attribute.Entry> list = pcMap.get(pc);
|
||||
private void put(int pc, LocalVariableInfo entry) {
|
||||
var list = pcMap.get(pc);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
pcMap.put(pc, list);
|
||||
@ -153,6 +136,6 @@ public class LocalVariableTableWriter extends InstructionDetailWriter {
|
||||
}
|
||||
|
||||
private ClassWriter classWriter;
|
||||
private Code_attribute codeAttr;
|
||||
private Map<Integer, List<LocalVariableTable_attribute.Entry>> pcMap;
|
||||
private CodeModel codeAttr;
|
||||
private Map<Integer, List<LocalVariableInfo>> pcMap;
|
||||
}
|
||||
|
@ -25,20 +25,15 @@
|
||||
|
||||
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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
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.
|
||||
@ -51,19 +46,21 @@ import java.util.Map;
|
||||
public class LocalVariableTypeTableWriter extends InstructionDetailWriter {
|
||||
public enum NoteKind {
|
||||
START("start") {
|
||||
public boolean match(LocalVariableTypeTable_attribute.Entry entry, int pc) {
|
||||
return (pc == entry.start_pc);
|
||||
@Override
|
||||
public boolean match(LocalVariableTypeInfo entry, int pc) {
|
||||
return (pc == entry.startPc());
|
||||
}
|
||||
},
|
||||
END("end") {
|
||||
public boolean match(LocalVariableTypeTable_attribute.Entry entry, int pc) {
|
||||
return (pc == entry.start_pc + entry.length);
|
||||
@Override
|
||||
public boolean match(LocalVariableTypeInfo entry, int pc) {
|
||||
return (pc == entry.startPc() + entry.length());
|
||||
}
|
||||
};
|
||||
NoteKind(String 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;
|
||||
}
|
||||
|
||||
@ -80,71 +77,60 @@ public class LocalVariableTypeTableWriter extends InstructionDetailWriter {
|
||||
classWriter = ClassWriter.instance(context);
|
||||
}
|
||||
|
||||
public void reset(Code_attribute attr) {
|
||||
public void reset(CodeModel attr) {
|
||||
codeAttr = attr;
|
||||
pcMap = new HashMap<>();
|
||||
LocalVariableTypeTable_attribute lvt =
|
||||
(LocalVariableTypeTable_attribute) (attr.attributes.get(Attribute.LocalVariableTypeTable));
|
||||
if (lvt == null)
|
||||
var lvt = attr.findAttribute(Attributes.LOCAL_VARIABLE_TYPE_TABLE);
|
||||
|
||||
if (lvt.isEmpty())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < lvt.local_variable_table.length; i++) {
|
||||
LocalVariableTypeTable_attribute.Entry entry = lvt.local_variable_table[i];
|
||||
put(entry.start_pc, entry);
|
||||
put(entry.start_pc + entry.length, entry);
|
||||
for (var entry : lvt.get().localVariableTypes()) {
|
||||
put(entry.startPc(), entry);
|
||||
put(entry.startPc() + entry.length(), entry);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeDetails(Instruction instr) {
|
||||
int pc = instr.getPC();
|
||||
@Override
|
||||
public void writeDetails(int pc, Instruction instr) {
|
||||
writeLocalVariables(pc, NoteKind.END);
|
||||
writeLocalVariables(pc, NoteKind.START);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
int pc = codeAttr.code_length;
|
||||
public void flush(int pc) {
|
||||
writeLocalVariables(pc, NoteKind.END);
|
||||
}
|
||||
|
||||
public void writeLocalVariables(int pc, NoteKind kind) {
|
||||
ConstantPool constant_pool = classWriter.getClassFile().constant_pool;
|
||||
String indent = space(2); // get from Options?
|
||||
List<LocalVariableTypeTable_attribute.Entry> entries = pcMap.get(pc);
|
||||
var entries = pcMap.get(pc);
|
||||
if (entries != null) {
|
||||
for (ListIterator<LocalVariableTypeTable_attribute.Entry> iter =
|
||||
entries.listIterator(kind == NoteKind.END ? entries.size() : 0);
|
||||
for (var iter = entries.listIterator(kind == NoteKind.END ? entries.size() : 0);
|
||||
kind == NoteKind.END ? iter.hasPrevious() : iter.hasNext() ; ) {
|
||||
LocalVariableTypeTable_attribute.Entry entry =
|
||||
kind == NoteKind.END ? iter.previous() : iter.next();
|
||||
var entry = kind == NoteKind.END ? iter.previous() : iter.next();
|
||||
if (kind.match(entry, pc)) {
|
||||
print(indent);
|
||||
print(kind.text);
|
||||
print(" generic local ");
|
||||
print(entry.index);
|
||||
print(entry.slot());
|
||||
print(" // ");
|
||||
Descriptor d = new Signature(entry.signature_index);
|
||||
try {
|
||||
print(d.getFieldType(constant_pool).replace("/", "."));
|
||||
} catch (InvalidDescriptor e) {
|
||||
print(report(e));
|
||||
} catch (ConstantPoolException e) {
|
||||
print(classWriter.sigPrinter.print(Signature.parseFrom(
|
||||
entry.signature().stringValue())).replace("/", "."));
|
||||
} catch (Exception e) {
|
||||
print(report(e));
|
||||
}
|
||||
print(" ");
|
||||
try {
|
||||
print(constant_pool.getUTF8Value(entry.name_index));
|
||||
} catch (ConstantPoolException e) {
|
||||
print(report(e));
|
||||
}
|
||||
print(entry.name().stringValue());
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void put(int pc, LocalVariableTypeTable_attribute.Entry entry) {
|
||||
List<LocalVariableTypeTable_attribute.Entry> list = pcMap.get(pc);
|
||||
private void put(int pc, LocalVariableTypeInfo entry) {
|
||||
var list = pcMap.get(pc);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
pcMap.put(pc, list);
|
||||
@ -154,6 +140,6 @@ public class LocalVariableTypeTableWriter extends InstructionDetailWriter {
|
||||
}
|
||||
|
||||
private ClassWriter classWriter;
|
||||
private Code_attribute codeAttr;
|
||||
private Map<Integer, List<LocalVariableTypeTable_attribute.Entry>> pcMap;
|
||||
private CodeModel codeAttr;
|
||||
private Map<Integer, List<LocalVariableTypeInfo>> pcMap;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
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
|
||||
@ -54,16 +54,16 @@ public class Options {
|
||||
/**
|
||||
* 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 isProtected = flags.is(AccessFlags.ACC_PROTECTED);
|
||||
boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE);
|
||||
boolean isPublic = (flags & ACC_PUBLIC) != 0;
|
||||
boolean isProtected = (flags & ACC_PROTECTED) != 0;
|
||||
boolean isPrivate = (flags & ACC_PRIVATE) != 0;
|
||||
boolean isPackage = !(isPublic || isProtected || isPrivate);
|
||||
|
||||
if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage))
|
||||
if ((showAccess == ACC_PUBLIC) && (isProtected || isPrivate || isPackage))
|
||||
return false;
|
||||
else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage))
|
||||
else if ((showAccess == ACC_PROTECTED) && (isPrivate || isPackage))
|
||||
return false;
|
||||
else if ((showAccess == 0) && (isPrivate))
|
||||
return false;
|
||||
|
@ -30,7 +30,6 @@ import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
@ -40,13 +39,10 @@ import javax.tools.JavaFileManager.Location;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
import com.sun.tools.classfile.Attribute;
|
||||
import com.sun.tools.classfile.ClassFile;
|
||||
import com.sun.tools.classfile.Code_attribute;
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
import com.sun.tools.classfile.Instruction;
|
||||
import com.sun.tools.classfile.LineNumberTable_attribute;
|
||||
import com.sun.tools.classfile.SourceFile_attribute;
|
||||
import jdk.internal.classfile.Attributes;
|
||||
import jdk.internal.classfile.ClassModel;
|
||||
import jdk.internal.classfile.CodeModel;
|
||||
import jdk.internal.classfile.Instruction;
|
||||
|
||||
|
||||
/**
|
||||
@ -74,14 +70,15 @@ public class SourceWriter extends InstructionDetailWriter {
|
||||
this.fileManager = fileManager;
|
||||
}
|
||||
|
||||
public void reset(ClassFile cf, Code_attribute attr) {
|
||||
setSource(cf);
|
||||
public void reset(CodeModel attr) {
|
||||
setSource(attr.parent().get().parent().get());
|
||||
setLineMap(attr);
|
||||
}
|
||||
|
||||
public void writeDetails(Instruction instr) {
|
||||
@Override
|
||||
public void writeDetails(int pc, Instruction instr) {
|
||||
String indent = space(40); // could get from Options?
|
||||
Set<Integer> lines = lineMap.get(instr.getPC());
|
||||
var lines = lineMap.get(pc);
|
||||
if (lines != null) {
|
||||
for (int line: lines) {
|
||||
print(indent);
|
||||
@ -105,37 +102,34 @@ public class SourceWriter extends InstructionDetailWriter {
|
||||
return (sourceLines.length > 0);
|
||||
}
|
||||
|
||||
private void setLineMap(Code_attribute attr) {
|
||||
private void setLineMap(CodeModel attr) {
|
||||
SortedMap<Integer, SortedSet<Integer>> map = new TreeMap<>();
|
||||
SortedSet<Integer> allLines = new TreeSet<>();
|
||||
for (Attribute a: attr.attributes) {
|
||||
if (a instanceof LineNumberTable_attribute) {
|
||||
LineNumberTable_attribute t = (LineNumberTable_attribute) a;
|
||||
for (LineNumberTable_attribute.Entry e: t.line_number_table) {
|
||||
int start_pc = e.start_pc;
|
||||
int line = e.line_number;
|
||||
SortedSet<Integer> pcLines = map.get(start_pc);
|
||||
if (pcLines == null) {
|
||||
pcLines = new TreeSet<>();
|
||||
map.put(start_pc, pcLines);
|
||||
}
|
||||
pcLines.add(line);
|
||||
allLines.add(line);
|
||||
for (var t : attr.findAttributes(Attributes.LINE_NUMBER_TABLE)) {
|
||||
for (var e: t.lineNumbers()) {
|
||||
int start_pc = e.startPc();
|
||||
int line = e.lineNumber();
|
||||
SortedSet<Integer> pcLines = map.get(start_pc);
|
||||
if (pcLines == null) {
|
||||
pcLines = new TreeSet<>();
|
||||
map.put(start_pc, pcLines);
|
||||
}
|
||||
pcLines.add(line);
|
||||
allLines.add(line);
|
||||
}
|
||||
}
|
||||
lineMap = map;
|
||||
lineList = new ArrayList<>(allLines);
|
||||
}
|
||||
|
||||
private void setSource(ClassFile cf) {
|
||||
private void setSource(ClassModel cf) {
|
||||
if (cf != classFile) {
|
||||
classFile = cf;
|
||||
sourceLines = splitLines(readSource(cf));
|
||||
}
|
||||
}
|
||||
|
||||
private String readSource(ClassFile cf) {
|
||||
private String readSource(ClassModel cf) {
|
||||
if (fileManager == null)
|
||||
return null;
|
||||
|
||||
@ -150,14 +144,13 @@ public class SourceWriter extends InstructionDetailWriter {
|
||||
// additional classes to determine the outmost class from any
|
||||
// InnerClasses and EnclosingMethod attributes.
|
||||
try {
|
||||
String className = cf.getName();
|
||||
SourceFile_attribute sf =
|
||||
(SourceFile_attribute) cf.attributes.get(Attribute.SourceFile);
|
||||
if (sf == null) {
|
||||
String className = cf.thisClass().asInternalName();
|
||||
var sf = cf.findAttribute(Attributes.SOURCE_FILE);
|
||||
if (sf.isEmpty()) {
|
||||
report(messages.getMessage("err.no.SourceFile.attribute"));
|
||||
return null;
|
||||
}
|
||||
String sourceFile = sf.getSourceFile(cf.constant_pool);
|
||||
String sourceFile = sf.get().sourceFile().stringValue();
|
||||
String fileBase = sourceFile.endsWith(".java")
|
||||
? sourceFile.substring(0, sourceFile.length() - 5) : sourceFile;
|
||||
int sep = className.lastIndexOf("/");
|
||||
@ -172,9 +165,6 @@ public class SourceWriter extends InstructionDetailWriter {
|
||||
return null;
|
||||
}
|
||||
return fo.getCharContent(true).toString();
|
||||
} catch (ConstantPoolException e) {
|
||||
report(e);
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
report(e.getLocalizedMessage());
|
||||
return null;
|
||||
@ -205,7 +195,7 @@ public class SourceWriter extends InstructionDetailWriter {
|
||||
}
|
||||
|
||||
private JavaFileManager fileManager;
|
||||
private ClassFile classFile;
|
||||
private ClassModel classFile;
|
||||
private SortedMap<Integer, SortedSet<Integer>> lineMap;
|
||||
private List<Integer> lineList;
|
||||
private String[] sourceLines;
|
||||
|
@ -25,23 +25,16 @@
|
||||
|
||||
package com.sun.tools.javap;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import jdk.internal.classfile.Attributes;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
|
||||
import com.sun.tools.classfile.AccessFlags;
|
||||
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.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.*;
|
||||
import jdk.internal.classfile.Instruction;
|
||||
import jdk.internal.classfile.attribute.CodeAttribute;
|
||||
import jdk.internal.classfile.attribute.StackMapFrameInfo;
|
||||
import jdk.internal.classfile.attribute.StackMapTableAttribute;
|
||||
|
||||
/**
|
||||
* Annotate instructions with stack map.
|
||||
@ -62,228 +55,108 @@ public class StackMapWriter extends InstructionDetailWriter {
|
||||
protected StackMapWriter(Context context) {
|
||||
super(context);
|
||||
context.put(StackMapWriter.class, this);
|
||||
classWriter = ClassWriter.instance(context);
|
||||
}
|
||||
|
||||
public void reset(Code_attribute attr) {
|
||||
setStackMap((StackMapTable_attribute) attr.attributes.get(Attribute.StackMapTable));
|
||||
public void reset(CodeAttribute code) {
|
||||
setStackMap(code);
|
||||
}
|
||||
|
||||
void setStackMap(StackMapTable_attribute attr) {
|
||||
void setStackMap(CodeAttribute code) {
|
||||
StackMapTableAttribute attr = code.findAttribute(Attributes.STACK_MAP_TABLE)
|
||||
.orElse(null);
|
||||
if (attr == null) {
|
||||
map = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Method m = classWriter.getMethod();
|
||||
Descriptor d = m.descriptor;
|
||||
String[] args;
|
||||
try {
|
||||
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(".", "/"));
|
||||
var m = code.parent().get();
|
||||
if ((m.flags().flagsMask() & Classfile.ACC_STATIC) == 0) {
|
||||
thisClassName = m.parent().get().thisClass().asInternalName();
|
||||
} else {
|
||||
thisClassName = null;
|
||||
}
|
||||
|
||||
map = new HashMap<>();
|
||||
StackMapBuilder builder = new StackMapBuilder();
|
||||
|
||||
// using -1 as the pc for the initial frame effectively compensates for
|
||||
// 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);
|
||||
this.code = code;
|
||||
for (var fr : attr.entries())
|
||||
map.put(code.labelToBci(fr.target()), fr);
|
||||
}
|
||||
|
||||
public void writeInitialDetails() {
|
||||
writeDetails(-1);
|
||||
}
|
||||
|
||||
public void writeDetails(Instruction instr) {
|
||||
writeDetails(instr.getPC());
|
||||
@Override
|
||||
public void writeDetails(int pc, Instruction instr) {
|
||||
writeDetails(pc);
|
||||
}
|
||||
|
||||
private void writeDetails(int pc) {
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
StackMap m = map.get(pc);
|
||||
var m = map.get(pc);
|
||||
if (m != null) {
|
||||
print("StackMap locals: ", m.locals);
|
||||
print("StackMap stack: ", m.stack);
|
||||
print("StackMap locals: ", m.locals(), true);
|
||||
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);
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
for (var e : entries) {
|
||||
print(" ");
|
||||
print(entries[i]);
|
||||
print(e, firstThis);
|
||||
firstThis = false;
|
||||
}
|
||||
println();
|
||||
}
|
||||
|
||||
void print(verification_type_info entry) {
|
||||
void print(StackMapFrameInfo.VerificationTypeInfo entry, boolean firstThis) {
|
||||
if (entry == null) {
|
||||
print("ERROR");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (entry.tag) {
|
||||
case -1:
|
||||
print(((CustomVerificationTypeInfo) entry).text);
|
||||
break;
|
||||
switch (entry) {
|
||||
case StackMapFrameInfo.SimpleVerificationTypeInfo s -> {
|
||||
switch (s) {
|
||||
case ITEM_TOP ->
|
||||
print("top");
|
||||
|
||||
case ITEM_Top:
|
||||
print("top");
|
||||
break;
|
||||
case ITEM_INTEGER ->
|
||||
print("int");
|
||||
|
||||
case ITEM_Integer:
|
||||
print("int");
|
||||
break;
|
||||
case ITEM_FLOAT ->
|
||||
print("float");
|
||||
|
||||
case ITEM_Float:
|
||||
print("float");
|
||||
break;
|
||||
case ITEM_LONG ->
|
||||
print("long");
|
||||
|
||||
case ITEM_Long:
|
||||
print("long");
|
||||
break;
|
||||
case ITEM_DOUBLE ->
|
||||
print("double");
|
||||
|
||||
case ITEM_Double:
|
||||
print("double");
|
||||
break;
|
||||
case ITEM_NULL ->
|
||||
print("null");
|
||||
|
||||
case ITEM_Null:
|
||||
print("null");
|
||||
break;
|
||||
|
||||
case ITEM_UninitializedThis:
|
||||
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("??");
|
||||
case ITEM_UNINITIALIZED_THIS ->
|
||||
print("uninit_this");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_Uninitialized:
|
||||
print(((Uninitialized_variable_info) entry).offset);
|
||||
break;
|
||||
case StackMapFrameInfo.ObjectVerificationTypeInfo o -> {
|
||||
String cln = o.className().asInternalName();
|
||||
print(firstThis && cln.equals(thisClassName) ? "this" : cln);
|
||||
}
|
||||
|
||||
case StackMapFrameInfo.UninitializedVerificationTypeInfo u ->
|
||||
print(code.labelToBci(u.newTarget()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Map<Integer, StackMap> map;
|
||||
private ClassWriter classWriter;
|
||||
|
||||
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 = { };
|
||||
private Map<Integer, StackMapFrameInfo> map;
|
||||
private String thisClassName;
|
||||
private CodeAttribute code;
|
||||
}
|
||||
|
@ -25,14 +25,13 @@
|
||||
|
||||
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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
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.
|
||||
@ -45,24 +44,24 @@ import java.util.Map;
|
||||
public class TryBlockWriter extends InstructionDetailWriter {
|
||||
public enum NoteKind {
|
||||
START("try") {
|
||||
public boolean match(Exception_data entry, int pc) {
|
||||
return (pc == entry.start_pc);
|
||||
public boolean match(ExceptionCatch entry, int pc, CodeAttribute lr) {
|
||||
return (pc == lr.labelToBci(entry.tryStart()));
|
||||
}
|
||||
},
|
||||
END("end try") {
|
||||
public boolean match(Exception_data entry, int pc) {
|
||||
return (pc == entry.end_pc);
|
||||
public boolean match(ExceptionCatch entry, int pc, CodeAttribute lr) {
|
||||
return (pc == lr.labelToBci(entry.tryEnd()));
|
||||
}
|
||||
},
|
||||
HANDLER("catch") {
|
||||
public boolean match(Exception_data entry, int pc) {
|
||||
return (pc == entry.handler_pc);
|
||||
public boolean match(ExceptionCatch entry, int pc, CodeAttribute lr) {
|
||||
return (pc == lr.labelToBci(entry.handler()));
|
||||
}
|
||||
};
|
||||
NoteKind(String 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;
|
||||
}
|
||||
|
||||
@ -79,46 +78,49 @@ public class TryBlockWriter extends InstructionDetailWriter {
|
||||
constantWriter = ConstantWriter.instance(context);
|
||||
}
|
||||
|
||||
public void reset(Code_attribute attr) {
|
||||
public void reset(CodeAttribute attr) {
|
||||
indexMap = new HashMap<>();
|
||||
pcMap = new HashMap<>();
|
||||
for (int i = 0; i < attr.exception_table.length; i++) {
|
||||
Exception_data entry = attr.exception_table[i];
|
||||
lr = attr;
|
||||
var excs = attr.exceptionHandlers();
|
||||
for (int i = 0; i < excs.size(); i++) {
|
||||
var entry = excs.get(i);
|
||||
indexMap.put(entry, i);
|
||||
put(entry.start_pc, entry);
|
||||
put(entry.end_pc, entry);
|
||||
put(entry.handler_pc, entry);
|
||||
put(lr.labelToBci(entry.tryStart()), entry);
|
||||
put(lr.labelToBci(entry.tryEnd()), entry);
|
||||
put(lr.labelToBci(entry.handler()), entry);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeDetails(Instruction instr) {
|
||||
writeTrys(instr, NoteKind.END);
|
||||
writeTrys(instr, NoteKind.START);
|
||||
writeTrys(instr, NoteKind.HANDLER);
|
||||
@Override
|
||||
public void writeDetails(int pc, Instruction instr) {
|
||||
writeTrys(pc, instr, NoteKind.END);
|
||||
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?
|
||||
int pc = instr.getPC();
|
||||
List<Exception_data> entries = pcMap.get(pc);
|
||||
var entries = pcMap.get(pc);
|
||||
if (entries != null) {
|
||||
for (ListIterator<Exception_data> iter =
|
||||
for (var iter =
|
||||
entries.listIterator(kind == NoteKind.END ? entries.size() : 0);
|
||||
kind == NoteKind.END ? iter.hasPrevious() : iter.hasNext() ; ) {
|
||||
Exception_data entry =
|
||||
var entry =
|
||||
kind == NoteKind.END ? iter.previous() : iter.next();
|
||||
if (kind.match(entry, pc)) {
|
||||
if (kind.match(entry, pc, lr)) {
|
||||
print(indent);
|
||||
print(kind.text);
|
||||
print("[");
|
||||
print(indexMap.get(entry));
|
||||
print("] ");
|
||||
if (entry.catch_type == 0)
|
||||
var ct = entry.catchType();
|
||||
if (ct.isEmpty())
|
||||
print("finally");
|
||||
else {
|
||||
print("#" + entry.catch_type);
|
||||
print("#" + ct.get().index());
|
||||
print(" // ");
|
||||
constantWriter.write(entry.catch_type);
|
||||
constantWriter.write(ct.get().index());
|
||||
}
|
||||
println();
|
||||
}
|
||||
@ -126,8 +128,8 @@ public class TryBlockWriter extends InstructionDetailWriter {
|
||||
}
|
||||
}
|
||||
|
||||
private void put(int pc, Exception_data entry) {
|
||||
List<Exception_data> list = pcMap.get(pc);
|
||||
private void put(int pc, ExceptionCatch entry) {
|
||||
var list = pcMap.get(pc);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
pcMap.put(pc, list);
|
||||
@ -136,7 +138,8 @@ public class TryBlockWriter extends InstructionDetailWriter {
|
||||
list.add(entry);
|
||||
}
|
||||
|
||||
private Map<Integer, List<Exception_data>> pcMap;
|
||||
private Map<Exception_data, Integer> indexMap;
|
||||
private Map<Integer, List<ExceptionCatch>> pcMap;
|
||||
private Map<ExceptionCatch, Integer> indexMap;
|
||||
private ConstantWriter constantWriter;
|
||||
private CodeAttribute lr;
|
||||
}
|
||||
|
@ -25,20 +25,17 @@
|
||||
|
||||
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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
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.
|
||||
@ -74,28 +71,37 @@ public class TypeAnnotationWriter extends InstructionDetailWriter {
|
||||
classWriter = ClassWriter.instance(context);
|
||||
}
|
||||
|
||||
public void reset(Code_attribute attr) {
|
||||
Method m = classWriter.getMethod();
|
||||
public void reset(CodeAttribute attr) {
|
||||
MethodModel m = attr.parent().get();
|
||||
pcMap = new HashMap<>();
|
||||
check(NoteKind.VISIBLE, (RuntimeVisibleTypeAnnotations_attribute) m.attributes.get(Attribute.RuntimeVisibleTypeAnnotations));
|
||||
check(NoteKind.INVISIBLE, (RuntimeInvisibleTypeAnnotations_attribute) m.attributes.get(Attribute.RuntimeInvisibleTypeAnnotations));
|
||||
codeAttribute = attr;
|
||||
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) {
|
||||
if (attr == null)
|
||||
private void check(NoteKind kind, Optional<List<TypeAnnotation>> annos) {
|
||||
if (annos.isEmpty())
|
||||
return;
|
||||
|
||||
for (TypeAnnotation anno: attr.annotations) {
|
||||
Position p = anno.position;
|
||||
Note note = null;
|
||||
if (p.offset != -1)
|
||||
addNote(p.offset, note = new Note(kind, anno));
|
||||
if (p.lvarOffset != null) {
|
||||
for (int i = 0; i < p.lvarOffset.length; i++) {
|
||||
if (note == null)
|
||||
note = new Note(kind, anno);
|
||||
addNote(p.lvarOffset[i], note);
|
||||
for (TypeAnnotation anno: annos.get()) {
|
||||
switch (anno.targetInfo()) {
|
||||
case TypeAnnotation.LocalVarTarget p -> {
|
||||
Note note = null;
|
||||
for (var lvar : p.table()) {
|
||||
if (note == null)
|
||||
note = new Note(kind, anno);
|
||||
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
|
||||
void writeDetails(Instruction instr) {
|
||||
void writeDetails(int pc, Instruction instr) {
|
||||
String indent = space(2); // get from Options?
|
||||
int pc = instr.getPC();
|
||||
List<Note> notes = pcMap.get(pc);
|
||||
if (notes != null) {
|
||||
for (Note n: notes) {
|
||||
print(indent);
|
||||
print("@");
|
||||
annotationWriter.write(n.anno, false, true);
|
||||
annotationWriter.write(n.anno, false, true, codeAttribute);
|
||||
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 ClassWriter classWriter;
|
||||
private Map<Integer, List<Note>> pcMap;
|
||||
private CodeAttribute codeAttribute;
|
||||
}
|
||||
|
@ -130,6 +130,7 @@ serviceability/sa/ClhsdbPmap.java#core 8267433 macosx-x64
|
||||
serviceability/sa/ClhsdbPstack.java#core 8267433 macosx-x64
|
||||
serviceability/sa/TestJmapCore.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
|
||||
|
||||
|
@ -30,13 +30,21 @@
|
||||
* @modules jdk.jdeps/com.sun.tools.javap
|
||||
*/
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class T8260403 {
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
if (com.sun.tools.javap.Main.run(new String[]{"-c", System.getProperty("test.classes") + "/InvalidSignature.class"},
|
||||
new PrintWriter(System.out)) != 0) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
var sw = new StringWriter();
|
||||
int res = com.sun.tools.javap.Main.run(
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,9 @@ public class T6866657
|
||||
JavapTask t = new JavapTask(log, fileManager, null);
|
||||
t.handleOptions(new String[] { "-sysinfo", className });
|
||||
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) {
|
||||
e.printStackTrace();
|
||||
|
@ -56,7 +56,9 @@ public class T7186925
|
||||
JavapTask t = new JavapTask(null, fileManager, null);
|
||||
t.handleOptions(new String[] { "-sysinfo", className });
|
||||
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) {
|
||||
ee.printStackTrace();
|
||||
|
54
test/langtools/tools/javap/malformed/Malformed.jcod
Normal file
54
test/langtools/tools/javap/malformed/Malformed.jcod
Normal 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
|
||||
|
50
test/langtools/tools/javap/malformed/MalformedTest.java
Normal file
50
test/langtools/tools/javap/malformed/MalformedTest.java
Normal 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");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user