530 lines
18 KiB
Java
530 lines
18 KiB
Java
/*
|
|
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
package org.openjdk.asmtools.jdis;
|
|
|
|
import org.openjdk.asmtools.asmutils.HexUtils;
|
|
import org.openjdk.asmtools.common.Tool;
|
|
import org.openjdk.asmtools.jasm.Modifiers;
|
|
|
|
import java.io.*;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.stream.Collectors;
|
|
|
|
import static java.lang.String.format;
|
|
import static org.openjdk.asmtools.jasm.RuntimeConstants.*;
|
|
import static org.openjdk.asmtools.jasm.Tables.*;
|
|
|
|
/**
|
|
* Central class data for of the Java Disassembler
|
|
*/
|
|
public class ClassData extends MemberData {
|
|
|
|
// Owner of this ClassData
|
|
protected Tool tool;
|
|
|
|
// -----------------------------
|
|
// Header Info
|
|
// -----------------------------
|
|
// Version info
|
|
protected int minor_version, major_version;
|
|
|
|
// Constant Pool index to this class
|
|
protected int this_cpx;
|
|
|
|
// Constant Pool index to this classes parent (super)
|
|
protected int super_cpx;
|
|
|
|
// Constant Pool index to a file reference to the Java source
|
|
protected int source_cpx = 0;
|
|
|
|
// -----------------------------
|
|
// The Constant Pool
|
|
// -----------------------------
|
|
protected ConstantPool pool;
|
|
|
|
// -----------------------------
|
|
// Interfaces,Fields,Methods && Attributes
|
|
// -----------------------------
|
|
// The interfaces this class implements
|
|
protected int[] interfaces;
|
|
|
|
// The fields of this class
|
|
protected ArrayList<FieldData> fields;
|
|
|
|
// The methods of this class
|
|
protected ArrayList<MethodData> methods;
|
|
|
|
// The record attribute of this class (since class file 58.65535)
|
|
protected RecordData record;
|
|
|
|
// The inner-classes of this class
|
|
protected ArrayList<InnerClassData> innerClasses;
|
|
|
|
// The bootstrapmethods this class implements
|
|
protected ArrayList<BootstrapMethodData> bootstrapMethods;
|
|
|
|
//The module this class file presents
|
|
protected ModuleData moduleData;
|
|
|
|
// The NestHost of this class (since class file: 55.0)
|
|
protected NestHostData nestHost;
|
|
|
|
// The NestMembers of this class (since class file: 55.0)
|
|
protected NestMembersData nestMembers;
|
|
|
|
// The PermittedSubtypes of this class (JEP 360 (Sealed types): class file 59.65535)
|
|
protected PermittedSubtypesData permittedSubtypes;
|
|
|
|
// other parsing fields
|
|
protected PrintWriter out;
|
|
protected String pkgPrefix = "";
|
|
// source file data
|
|
private TextLines sourceLines = null;
|
|
private Path classFile = null;
|
|
|
|
public ClassData(PrintWriter out, Tool tool) {
|
|
this.out = out;
|
|
this.tool = tool;
|
|
memberType = "ClassData";
|
|
TraceUtils.traceln("printOptions=" + options.toString());
|
|
pool = new ConstantPool(this);
|
|
init(this);
|
|
}
|
|
|
|
public void read(File in) throws IOException {
|
|
try ( DataInputStream dis = new DataInputStream(new FileInputStream(in))){
|
|
read(dis);
|
|
}
|
|
classFile = in.toPath();
|
|
}
|
|
|
|
public void read(String in) throws IOException {
|
|
try ( DataInputStream dis = new DataInputStream(new FileInputStream(in))){
|
|
read(dis);
|
|
}
|
|
classFile = Paths.get(in);
|
|
}
|
|
|
|
/**
|
|
* Read and resolve the field data
|
|
*/
|
|
protected void readFields(DataInputStream in) throws IOException {
|
|
int nfields = in.readUnsignedShort();
|
|
TraceUtils.traceln("nfields=" + nfields);
|
|
fields = new ArrayList<>(nfields);
|
|
for (int k = 0; k < nfields; k++) {
|
|
FieldData field = new FieldData(this);
|
|
TraceUtils.traceln(" FieldData: #" + k);
|
|
field.read(in);
|
|
fields.add(field);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read and resolve the method data
|
|
*/
|
|
protected void readMethods(DataInputStream in) throws IOException {
|
|
int nmethods = in.readUnsignedShort();
|
|
TraceUtils.traceln("nmethods=" + nmethods);
|
|
methods = new ArrayList<>(nmethods);
|
|
for (int k = 0; k < nmethods; k++) {
|
|
MethodData method = new MethodData(this);
|
|
TraceUtils.traceln(" MethodData: #" + k);
|
|
method.read(in);
|
|
methods.add(method);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read and resolve the interface data
|
|
*/
|
|
protected void readInterfaces(DataInputStream in) throws IOException {
|
|
// Read the interface names
|
|
int numinterfaces = in.readUnsignedShort();
|
|
TraceUtils.traceln("numinterfaces=" + numinterfaces);
|
|
interfaces = new int[numinterfaces];
|
|
for (int i = 0; i < numinterfaces; i++) {
|
|
int intrf_cpx = in.readShort();
|
|
TraceUtils.traceln(" intrf_cpx[" + i + "]=" + intrf_cpx);
|
|
interfaces[i] = intrf_cpx;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read and resolve the attribute data
|
|
*/
|
|
@Override
|
|
protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException {
|
|
// Read the Attributes
|
|
boolean handled = true;
|
|
switch (attrtag) {
|
|
case ATT_SourceFile:
|
|
// Read SourceFile Attr
|
|
if (attrlen != 2) {
|
|
throw new ClassFormatError("ATT_SourceFile: Invalid attribute length");
|
|
}
|
|
source_cpx = in.readUnsignedShort();
|
|
break;
|
|
case ATT_InnerClasses:
|
|
// Read InnerClasses Attr
|
|
int num1 = in.readUnsignedShort();
|
|
if (2 + num1 * 8 != attrlen) {
|
|
throw new ClassFormatError("ATT_InnerClasses: Invalid attribute length");
|
|
}
|
|
innerClasses = new ArrayList<>(num1);
|
|
for (int j = 0; j < num1; j++) {
|
|
InnerClassData innerClass = new InnerClassData(this);
|
|
innerClass.read(in);
|
|
innerClasses.add(innerClass);
|
|
}
|
|
break;
|
|
case ATT_BootstrapMethods:
|
|
// Read BootstrapMethods Attr
|
|
int num2 = in.readUnsignedShort();
|
|
bootstrapMethods = new ArrayList<>(num2);
|
|
for (int j = 0; j < num2; j++) {
|
|
BootstrapMethodData bsmData = new BootstrapMethodData(this);
|
|
bsmData.read(in);
|
|
bootstrapMethods.add(bsmData);
|
|
}
|
|
break;
|
|
case ATT_Module:
|
|
// Read Module Attribute
|
|
moduleData = new ModuleData(this);
|
|
moduleData.read(in);
|
|
break;
|
|
case ATT_NestHost:
|
|
// Read NestHost Attribute (since class file: 55.0)
|
|
nestHost = new NestHostData(this).read(in, attrlen);
|
|
break;
|
|
case ATT_NestMembers:
|
|
// Read NestMembers Attribute (since class file: 55.0)
|
|
nestMembers = new NestMembersData(this).read(in, attrlen);
|
|
break;
|
|
case ATT_Record:
|
|
record = new RecordData(this).read(in);
|
|
break;
|
|
case ATT_PermittedSubtypes:
|
|
// Read PermittedSubtypes Attribute (JEP 360 (Sealed types): class file 59.65535)
|
|
permittedSubtypes = new PermittedSubtypesData(this).read(in, attrlen);
|
|
break;
|
|
default:
|
|
handled = false;
|
|
break;
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
/**
|
|
* Read and resolve the class data
|
|
*/
|
|
private void read(DataInputStream in) throws IOException {
|
|
// Read the header
|
|
int magic = in.readInt();
|
|
if (magic != JAVA_MAGIC) {
|
|
throw new ClassFormatError("wrong magic: " + HexUtils.toHex(magic) + ", expected " + HexUtils.toHex(JAVA_MAGIC));
|
|
}
|
|
minor_version = in.readUnsignedShort();
|
|
major_version = in.readUnsignedShort();
|
|
|
|
// Read the constant pool
|
|
pool.read(in);
|
|
access = in.readUnsignedShort(); // & MM_CLASS; // Q
|
|
this_cpx = in.readUnsignedShort();
|
|
super_cpx = in.readUnsignedShort();
|
|
TraceUtils.traceln("access=" + access + " " + Modifiers.accessString(access, CF_Context.CTX_INNERCLASS) +
|
|
" this_cpx=" + this_cpx +
|
|
" super_cpx=" + super_cpx);
|
|
|
|
// Read the interfaces
|
|
readInterfaces(in);
|
|
|
|
// Read the fields
|
|
readFields(in);
|
|
|
|
// Read the methods
|
|
readMethods(in);
|
|
|
|
// Read the attributes
|
|
readAttributes(in);
|
|
//
|
|
TraceUtils.traceln("", "<< Reading is done >>", "");
|
|
}
|
|
|
|
/**
|
|
* Read and resolve the attribute data
|
|
*/
|
|
public String getSrcLine(int lnum) {
|
|
if (sourceLines == null) {
|
|
return null; // impossible call
|
|
}
|
|
String line;
|
|
try {
|
|
line = sourceLines.getLine(lnum);
|
|
} catch (ArrayIndexOutOfBoundsException e) {
|
|
line = "Line number " + lnum + " is out of bounds";
|
|
}
|
|
return line;
|
|
}
|
|
|
|
private <T extends AnnotationData> void printAnnotations(List<T> annotations) {
|
|
if (annotations != null) {
|
|
for (T ad : annotations) {
|
|
ad.print(out, "");
|
|
out.println();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void print() throws IOException {
|
|
int k, l;
|
|
String className = "";
|
|
String sourceName = null;
|
|
if( isModuleUnit() ) {
|
|
// Print the Annotations
|
|
printAnnotations(visibleAnnotations);
|
|
printAnnotations(invisibleAnnotations);
|
|
} else {
|
|
className = pool.getClassName(this_cpx);
|
|
int pkgPrefixLen = className.lastIndexOf("/") + 1;
|
|
// Write the header
|
|
// package-info compilation unit
|
|
if (className.endsWith("package-info")) {
|
|
// Print the Annotations
|
|
printAnnotations(visibleAnnotations);
|
|
printAnnotations(invisibleAnnotations);
|
|
printAnnotations(visibleTypeAnnotations);
|
|
printAnnotations(invisibleTypeAnnotations);
|
|
if (pkgPrefixLen != 0) {
|
|
pkgPrefix = className.substring(0, pkgPrefixLen);
|
|
out.print("package " + pkgPrefix.substring(0, pkgPrefixLen - 1) + " ");
|
|
out.print("version " + major_version + ":" + minor_version + ";");
|
|
}
|
|
out.println();
|
|
return;
|
|
}
|
|
if (pkgPrefixLen != 0) {
|
|
pkgPrefix = className.substring(0, pkgPrefixLen);
|
|
out.println("package " + pkgPrefix.substring(0, pkgPrefixLen - 1) + ";");
|
|
className = pool.getShortClassName(this_cpx, pkgPrefix);
|
|
}
|
|
out.println();
|
|
// Print the Annotations
|
|
printAnnotations(visibleAnnotations);
|
|
printAnnotations(invisibleAnnotations);
|
|
printAnnotations(visibleTypeAnnotations);
|
|
printAnnotations(invisibleTypeAnnotations);
|
|
if ((access & ACC_SUPER) != 0) {
|
|
out.print("super ");
|
|
access = access & ~ACC_SUPER;
|
|
}
|
|
}
|
|
// see if we are going to print: abstract interface class
|
|
// then replace it with just: interface
|
|
printHeader:
|
|
{
|
|
printSugar:
|
|
{
|
|
if ((access & ACC_ABSTRACT) == 0) {
|
|
break printSugar;
|
|
}
|
|
if ((access & ACC_INTERFACE) == 0) {
|
|
break printSugar;
|
|
}
|
|
if (options.contains(Options.PR.CPX)) {
|
|
break printSugar;
|
|
}
|
|
if (this_cpx == 0) {
|
|
break printSugar;
|
|
}
|
|
|
|
// make sure the this_class is a valid class ref
|
|
ConstantPool.Constant this_const = pool.getConst(this_cpx);
|
|
if (this_const == null || this_const.tag != ConstantPool.TAG.CONSTANT_CLASS) {
|
|
break printSugar;
|
|
}
|
|
|
|
// all conditions met, print syntactic sugar:
|
|
out.print(Modifiers.accessString(access & ~ACC_ABSTRACT, CF_Context.CTX_CLASS));
|
|
if (isSynthetic) {
|
|
out.print("synthetic ");
|
|
}
|
|
if (isDeprecated) {
|
|
out.print("deprecated ");
|
|
}
|
|
out.print(" " + pool.getShortClassName(this_cpx, pkgPrefix));
|
|
break printHeader;
|
|
}
|
|
|
|
if(isModuleUnit()) {
|
|
out.print(moduleData.getModuleHeader());
|
|
} else {
|
|
// not all conditions met, print header in ordinary way:
|
|
out.print(Modifiers.accessString(access, CF_Context.CTX_CLASS));
|
|
if (isSynthetic) {
|
|
out.print("synthetic ");
|
|
}
|
|
if (isDeprecated) {
|
|
out.print("deprecated ");
|
|
}
|
|
if (options.contains(Options.PR.CPX)) {
|
|
out.print("\t#" + this_cpx + " //");
|
|
}
|
|
pool.PrintConstant(out, this_cpx);
|
|
}
|
|
}
|
|
out.println();
|
|
if(!isModuleUnit()) {
|
|
if (!pool.getClassName(super_cpx).equals("java/lang/Object")) {
|
|
out.print("\textends ");
|
|
pool.printlnClassId(out, super_cpx);
|
|
out.println();
|
|
}
|
|
}
|
|
l = interfaces.length;
|
|
|
|
if (l > 0) {
|
|
for (k = 0; k < l; k++) {
|
|
if (k == 0) {
|
|
out.print("\timplements ");
|
|
} else {
|
|
out.print("\t\t ");
|
|
}
|
|
boolean printComma = (l > 1 && k < (l - 1));
|
|
pool.printlnClassId(out, interfaces[k], printComma);
|
|
out.println();
|
|
}
|
|
}
|
|
out.println("\tversion " + major_version + ":" + minor_version);
|
|
out.println("{");
|
|
|
|
if ((options.contains(Options.PR.SRC)) && (source_cpx != 0)) {
|
|
sourceName = pool.getString(source_cpx);
|
|
if (sourceName != null) {
|
|
sourceLines = new TextLines(classFile.getParent(), sourceName);
|
|
}
|
|
}
|
|
|
|
// Print the constant pool
|
|
if (options.contains(Options.PR.CP)) {
|
|
pool.print(out);
|
|
}
|
|
// Don't print fields, methods, inner classes and bootstrap methods if it is module-info entity
|
|
if ( !isModuleUnit() ) {
|
|
|
|
// Print the fields
|
|
printMemberDataList(fields);
|
|
|
|
// Print the methods
|
|
printMemberDataList(methods);
|
|
|
|
// Print the Record (since class file 58.65535 JEP 359)
|
|
if( record != null ) {
|
|
record.print();
|
|
}
|
|
|
|
// Print PermittedSubtypes Attribute (JEP 360 (Sealed types): class file 59.65535)
|
|
if( permittedSubtypes != null) {
|
|
permittedSubtypes.print();
|
|
}
|
|
// Print the NestHost (since class file: 55.0)
|
|
if(nestHost != null) {
|
|
nestHost.print();
|
|
}
|
|
// Print the NestMembers (since class file: 55.0)
|
|
if( nestMembers != null) {
|
|
nestMembers.print();
|
|
}
|
|
// Print the inner classes
|
|
if (innerClasses != null && !innerClasses.isEmpty()) {
|
|
for (InnerClassData icd : innerClasses) {
|
|
icd.print();
|
|
}
|
|
out.println();
|
|
}
|
|
// Print the BootstrapMethods
|
|
//
|
|
// Only print these if printing extended constants
|
|
if ((options.contains(Options.PR.CPX)) && bootstrapMethods != null && !bootstrapMethods.isEmpty()) {
|
|
for (BootstrapMethodData bsmdd : bootstrapMethods) {
|
|
bsmdd.print();
|
|
}
|
|
out.println();
|
|
}
|
|
out.println(format("} // end Class %s%s",
|
|
className,
|
|
sourceName != null ? " compiled from \"" + sourceName +"\"" : ""));
|
|
} else {
|
|
// Print module attributes
|
|
moduleData.print();
|
|
out.print("} // end Module ");
|
|
out.print( moduleData.getModuleName());
|
|
if(moduleData.getModuleVersion() != null)
|
|
out.print(" @" + moduleData.getModuleVersion());
|
|
out.println();
|
|
}
|
|
|
|
List<IOException> issues = getIssues();
|
|
if( !issues.isEmpty() ) {
|
|
|
|
throw issues.get(0);
|
|
}
|
|
} // end ClassData.print()
|
|
|
|
// Gets the type of processed binary
|
|
private boolean isModuleUnit() {
|
|
return moduleData != null;
|
|
}
|
|
|
|
private void printMemberDataList( List<? extends MemberData> list) throws IOException {
|
|
if( list != null ) {
|
|
int count = list.size();
|
|
if( count > 0 ) {
|
|
for( int i=0; i < count; i++ ) {
|
|
MemberData md = list.get(i);
|
|
md.setIndent(Options.BODY_INDENT);
|
|
if( i !=0 && md.getAnnotationsCount() > 0 )
|
|
out.println();
|
|
md.print();
|
|
}
|
|
out.println();
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<IOException> getIssues() {
|
|
return this.pool.pool.stream().
|
|
filter(Objects::nonNull).
|
|
filter(c->c.getIssue() != null).
|
|
map(ConstantPool.Constant::getIssue).
|
|
collect(Collectors.toList());
|
|
}
|
|
|
|
}// end class ClassData
|
|
|