asmtools/src/org/openjdk/asmtools/jdis/ClassData.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