/* * 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 fields; // The methods of this class protected ArrayList methods; // The record attribute of this class (since class file 58.65535) protected RecordData record; // The inner-classes of this class protected ArrayList innerClasses; // The bootstrapmethods this class implements protected ArrayList 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 void printAnnotations(List 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 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 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 getIssues() { return this.pool.pool.stream(). filter(Objects::nonNull). filter(c->c.getIssue() != null). map(ConstantPool.Constant::getIssue). collect(Collectors.toList()); } }// end class ClassData