1 /*
  2  * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package org.openjdk.asmtools.jdis;
 24 
 25 import org.openjdk.asmtools.asmutils.HexUtils;
 26 import org.openjdk.asmtools.common.Tool;
 27 import org.openjdk.asmtools.jasm.Modifiers;
 28 
 29 import java.io.*;
 30 import java.nio.file.Path;
 31 import java.nio.file.Paths;
 32 import java.util.ArrayList;
 33 import java.util.List;
 34 import java.util.Objects;
 35 import java.util.stream.Collectors;
 36 
 37 import static java.lang.String.format;
 38 import static org.openjdk.asmtools.jasm.RuntimeConstants.*;
 39 import static org.openjdk.asmtools.jasm.Tables.*;
 40 
 41 /**
 42  * Central class data for of the Java Disassembler
 43  */
 44 public class ClassData extends MemberData {
 45 
 46     // Owner of this ClassData
 47     protected Tool tool;
 48 
 49     // -----------------------------
 50     // Header Info
 51     // -----------------------------
 52     // Version info
 53     protected int minor_version, major_version;
 54 
 55     // Constant Pool index to this class
 56     protected int this_cpx;
 57 
 58     // Constant Pool index to this classes parent (super)
 59     protected int super_cpx;
 60 
 61     // Constant Pool index to a file reference to the Java source
 62     protected int source_cpx = 0;
 63 
 64     // -----------------------------
 65     // The Constant Pool
 66     // -----------------------------
 67     protected ConstantPool pool;
 68 
 69     // -----------------------------
 70     // Interfaces,Fields,Methods && Attributes
 71     // -----------------------------
 72     // The interfaces this class implements
 73     protected int[] interfaces;
 74 
 75     // The fields of this class
 76     protected ArrayList<FieldData> fields;
 77 
 78     // The methods of this class
 79     protected ArrayList<MethodData> methods;
 80 
 81     // The record attribute of this class (since class file 58.65535)
 82     protected RecordData record;
 83 
 84     // The inner-classes of this class
 85     protected ArrayList<InnerClassData> innerClasses;
 86 
 87     // The bootstrapmethods this class implements
 88     protected ArrayList<BootstrapMethodData> bootstrapMethods;
 89 
 90     //The module this class file presents
 91     protected ModuleData moduleData;
 92 
 93     // The NestHost of this class (since class file: 55.0)
 94     protected NestHostData nestHost;
 95 
 96     // The NestMembers of this class (since class file: 55.0)
 97     protected NestMembersData nestMembers;
 98 
 99     // The PermittedSubclasses of this class (JEP 360 (Sealed types): class file 59.65535)
100     protected PermittedSubclassesData permittedSubclassesData;
101 
102     protected PreloadData preloadData;
103 
104     // other parsing fields
105     protected PrintWriter out;
106     protected String pkgPrefix = "";
107     // source file data
108     private TextLines sourceLines = null;
109     private Path classFile = null;
110 
111     public ClassData(PrintWriter out, Tool tool) {
112         this.out  = out;
113         this.tool = tool;
114         memberType = "ClassData";
115         TraceUtils.traceln("printOptions=" + options.toString());
116         pool = new ConstantPool(this);
117         init(this);
118     }
119 
120     public void read(File in) throws IOException {
121         try ( DataInputStream dis = new DataInputStream(new FileInputStream(in))){
122             read(dis);
123         }
124         classFile = in.toPath();
125     }
126 
127     public void read(String in) throws IOException {
128         try ( DataInputStream dis = new DataInputStream(new FileInputStream(in))){
129             read(dis);
130         }
131         classFile = Paths.get(in);
132     }
133 
134     /**
135      * Read and resolve the field data
136      */
137     protected void readFields(DataInputStream in) throws IOException {
138         int nfields = in.readUnsignedShort();
139         TraceUtils.traceln("nfields=" + nfields);
140         fields = new ArrayList<>(nfields);
141         for (int k = 0; k < nfields; k++) {
142             FieldData field = new FieldData(this);
143             TraceUtils.traceln("  FieldData: #" + k);
144             field.read(in);
145             fields.add(field);
146         }
147     }
148 
149     /**
150      * Read and resolve the method data
151      */
152     protected void readMethods(DataInputStream in) throws IOException {
153         int nmethods = in.readUnsignedShort();
154         TraceUtils.traceln("nmethods=" + nmethods);
155         methods = new ArrayList<>(nmethods);
156         for (int k = 0; k < nmethods; k++) {
157             MethodData method = new MethodData(this);
158             TraceUtils.traceln("  MethodData: #" + k);
159             method.read(in);
160             methods.add(method);
161         }
162     }
163 
164     /**
165      * Read and resolve the interface data
166      */
167     protected void readInterfaces(DataInputStream in) throws IOException {
168         // Read the interface names
169         int numinterfaces = in.readUnsignedShort();
170         TraceUtils.traceln("numinterfaces=" + numinterfaces);
171         interfaces = new int[numinterfaces];
172         for (int i = 0; i < numinterfaces; i++) {
173             int intrf_cpx = in.readShort();
174             TraceUtils.traceln("  intrf_cpx[" + i + "]=" + intrf_cpx);
175             interfaces[i] = intrf_cpx;
176         }
177     }
178 
179     /**
180      * Read and resolve the attribute data
181      */
182     @Override
183     protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException {
184         // Read the Attributes
185         boolean handled = true;
186         switch (attrtag) {
187             case ATT_SourceFile:
188                 // Read SourceFile Attr
189                 if (attrlen != 2) {
190                     throw new ClassFormatError("ATT_SourceFile: Invalid attribute length");
191                 }
192                 source_cpx = in.readUnsignedShort();
193                 break;
194             case ATT_InnerClasses:
195                 // Read InnerClasses Attr
196                 int num1 = in.readUnsignedShort();
197                 if (2 + num1 * 8 != attrlen) {
198                     throw new ClassFormatError("ATT_InnerClasses: Invalid attribute length");
199                 }
200                 innerClasses = new ArrayList<>(num1);
201                 for (int j = 0; j < num1; j++) {
202                     InnerClassData innerClass = new InnerClassData(this);
203                     innerClass.read(in);
204                     innerClasses.add(innerClass);
205                 }
206                 break;
207             case ATT_BootstrapMethods:
208                 // Read BootstrapMethods Attr
209                 int num2 = in.readUnsignedShort();
210                 bootstrapMethods = new ArrayList<>(num2);
211                 for (int j = 0; j < num2; j++) {
212                     BootstrapMethodData bsmData = new BootstrapMethodData(this);
213                     bsmData.read(in);
214                     bootstrapMethods.add(bsmData);
215                 }
216                 break;
217             case ATT_Module:
218                 // Read Module Attribute
219                 moduleData = new ModuleData(this);
220                 moduleData.read(in);
221                 break;
222             case ATT_NestHost:
223                 // Read NestHost Attribute (since class file: 55.0)
224                 nestHost = new NestHostData(this).read(in, attrlen);
225                 break;
226             case ATT_NestMembers:
227                 // Read NestMembers Attribute (since class file: 55.0)
228                 nestMembers = new NestMembersData(this).read(in, attrlen);
229                 break;
230             case ATT_Record:
231                 record = new RecordData(this).read(in);
232                 break;
233             case ATT_PermittedSubclasses:
234                 // Read PermittedSubclasses Attribute (JEP 360 (Sealed types): class file 59.65535)
235                 permittedSubclassesData = new PermittedSubclassesData(this).read(in, attrlen);
236                 break;
237             case ATT_Preload:
238                 preloadData = new PreloadData(this).read(in, attrlen);
239                 break;
240             default:
241                 handled = false;
242                 break;
243         }
244         return handled;
245     }
246 
247     /**
248      * Read and resolve the class data
249      */
250     private void read(DataInputStream in) throws IOException {
251         // Read the header
252         int magic = in.readInt();
253         if (magic != JAVA_MAGIC) {
254             throw new ClassFormatError("wrong magic: " + HexUtils.toHex(magic) + ", expected " + HexUtils.toHex(JAVA_MAGIC));
255         }
256         minor_version = in.readUnsignedShort();
257         major_version = in.readUnsignedShort();
258 
259         // Read the constant pool
260         pool.read(in);
261         access = in.readUnsignedShort(); // & MM_CLASS; // Q
262         this_cpx = in.readUnsignedShort();
263         super_cpx = in.readUnsignedShort();
264         TraceUtils.traceln("access=" + access + " " + Modifiers.accessString(access, CF_Context.CTX_INNERCLASS) +
265                 " this_cpx=" + this_cpx +
266                 " super_cpx=" + super_cpx);
267 
268         // Read the interfaces
269         readInterfaces(in);
270 
271         // Read the fields
272         readFields(in);
273 
274         // Read the methods
275         readMethods(in);
276 
277         // Read the attributes
278         readAttributes(in);
279         //
280         TraceUtils.traceln("", "<< Reading is done >>", "");
281     }
282 
283     /**
284      * Read and resolve the attribute data
285      */
286     public String getSrcLine(int lnum) {
287         if (sourceLines == null) {
288             return null;  // impossible call
289         }
290         String line;
291         try {
292             line = sourceLines.getLine(lnum);
293         } catch (ArrayIndexOutOfBoundsException e) {
294             line = "Line number " + lnum + " is out of bounds";
295         }
296         return line;
297     }
298 
299     private <T extends AnnotationData> void printAnnotations(List<T> annotations) {
300         if (annotations != null) {
301             for (T ad : annotations) {
302                 ad.print(out, "");
303                 out.println();
304             }
305         }
306     }
307 
308     @Override
309     public void print() throws IOException {
310         int k, l;
311         String className = "";
312         String sourceName = null;
313         if( isModuleUnit() ) {
314             // Print the Annotations
315             printAnnotations(visibleAnnotations);
316             printAnnotations(invisibleAnnotations);
317         } else {
318             className = pool.getClassName(this_cpx);
319             int pkgPrefixLen = className.lastIndexOf("/") + 1;
320             // Write the header
321             // package-info compilation unit
322             if (className.endsWith("package-info")) {
323                 // Print the Annotations
324                 printAnnotations(visibleAnnotations);
325                 printAnnotations(invisibleAnnotations);
326                 printAnnotations(visibleTypeAnnotations);
327                 printAnnotations(invisibleTypeAnnotations);
328                 if (pkgPrefixLen != 0) {
329                     pkgPrefix = className.substring(0, pkgPrefixLen);
330                     out.print("package  " + pkgPrefix.substring(0, pkgPrefixLen - 1) + " ");
331                     out.print("version " + major_version + ":" + minor_version + ";");
332                 }
333                 out.println();
334                 return;
335             }
336             if (pkgPrefixLen != 0) {
337                 pkgPrefix = className.substring(0, pkgPrefixLen);
338                 out.println("package  " + pkgPrefix.substring(0, pkgPrefixLen - 1) + ";");
339                 className = pool.getShortClassName(this_cpx, pkgPrefix);
340             }
341             out.println();
342             // Print the Annotations
343             printAnnotations(visibleAnnotations);
344             printAnnotations(invisibleAnnotations);
345             printAnnotations(visibleTypeAnnotations);
346             printAnnotations(invisibleTypeAnnotations);
347             if ((access & ACC_SUPER) != 0) {
348                 out.print("super ");
349                 access = access & ~ACC_SUPER;
350             }
351         }
352 // see if we are going to print: abstract interface class
353 // then replace it with just: interface
354 printHeader:
355         {
356 printSugar:
357             {
358                 if ((access & ACC_ABSTRACT) == 0) {
359                     break printSugar;
360                 }
361                 if ((access & ACC_INTERFACE) == 0) {
362                     break printSugar;
363                 }
364                 if (options.contains(Options.PR.CPX)) {
365                     break printSugar;
366                 }
367                 if (this_cpx == 0) {
368                     break printSugar;
369                 }
370 
371                 // make sure the this_class is a valid class ref
372                 ConstantPool.Constant this_const = pool.getConst(this_cpx);
373                 if (this_const == null || this_const.tag != ConstantPool.TAG.CONSTANT_CLASS) {
374                     break printSugar;
375                 }
376 
377                 // all conditions met, print syntactic sugar:
378                 out.print(Modifiers.accessString(access & ~ACC_ABSTRACT, CF_Context.CTX_CLASS));
379                 if (isSynthetic) {
380                     out.print("synthetic ");
381                 }
382                 if (isDeprecated) {
383                     out.print("deprecated ");
384                 }
385                 out.print(" " + pool.getShortClassName(this_cpx, pkgPrefix));
386                 break printHeader;
387             }
388 
389             if(isModuleUnit()) {
390                 out.print(moduleData.getModuleHeader());
391             } else {
392                 // not all conditions met, print header in ordinary way:
393                 out.print(Modifiers.accessString(access, CF_Context.CTX_CLASS));
394                 if (isSynthetic) {
395                     out.print("synthetic ");
396                 }
397                 if (isDeprecated) {
398                     out.print("deprecated ");
399                 }
400                 if (options.contains(Options.PR.CPX)) {
401                     out.print("\t#" + this_cpx + " //");
402                 }
403                 pool.PrintConstant(out, this_cpx);
404             }
405         }
406         out.println();
407         if(!isModuleUnit()) {
408             if (!pool.getClassName(super_cpx).equals("java/lang/Object")) {
409                 out.print("\textends ");
410                 pool.printlnClassId(out, super_cpx);
411                 out.println();
412             }
413         }
414         l = interfaces.length;
415 
416         if (l > 0) {
417             for (k = 0; k < l; k++) {
418                 if (k == 0) {
419                     out.print("\timplements ");
420                 } else {
421                     out.print("\t\t ");
422                 }
423                 boolean printComma = (l > 1 && k < (l - 1));
424                 pool.printlnClassId(out, interfaces[k], printComma);
425                 out.println();
426             }
427         }
428         out.println("\tversion " + major_version + ":" + minor_version);
429         out.println("{");
430 
431         if ((options.contains(Options.PR.SRC)) && (source_cpx != 0)) {
432             sourceName = pool.getString(source_cpx);
433             if (sourceName != null) {
434                 sourceLines = new TextLines(classFile.getParent(), sourceName);
435             }
436         }
437 
438         // Print the constant pool
439         if (options.contains(Options.PR.CP)) {
440             pool.print(out);
441         }
442         // Don't print fields, methods, inner classes and bootstrap methods if it is module-info entity
443         if ( !isModuleUnit() ) {
444 
445             // Print the fields
446             printMemberDataList(fields);
447 
448             // Print the methods
449             printMemberDataList(methods);
450 
451             // Print the Record (since class file 58.65535 JEP 359)
452             if( record != null ) {
453                 record.print();
454             }
455 
456             // Print PermittedSubclasses Attribute (JEP 360 (Sealed types): class file 59.65535)
457             if( permittedSubclassesData != null) {
458                 permittedSubclassesData.print();
459             }
460             // Print the NestHost (since class file: 55.0)
461             if(nestHost != null) {
462                 nestHost.print();
463             }
464             // Print the NestMembers (since class file: 55.0)
465             if( nestMembers  != null) {
466                 nestMembers.print();
467             }
468             // Print the inner classes
469             if (innerClasses != null && !innerClasses.isEmpty()) {
470                 for (InnerClassData icd : innerClasses) {
471                     icd.print();
472                 }
473                 out.println();
474             }
475 
476             // Print Preload attribute
477             if (preloadData != null) {
478                 preloadData.print();
479             }
480 
481             // Print the BootstrapMethods
482             //
483             // Only print these if printing extended constants
484             if ((options.contains(Options.PR.CPX)) && bootstrapMethods != null && !bootstrapMethods.isEmpty()) {
485                 for (BootstrapMethodData bsmdd : bootstrapMethods) {
486                     bsmdd.print();
487                 }
488                 out.println();
489             }
490             out.println(format("} // end Class %s%s",
491                     className,
492                     sourceName != null ? " compiled from \"" + sourceName +"\"" : ""));
493         } else {
494             // Print module attributes
495             moduleData.print();
496             out.print("} // end Module ");
497             out.print( moduleData.getModuleName());
498             if(moduleData.getModuleVersion() != null)
499                 out.print(" @" + moduleData.getModuleVersion());
500             out.println();
501         }
502 
503         List<IOException> issues = getIssues();
504         if( !issues.isEmpty() ) {
505 
506             throw issues.get(0);
507         }
508     } // end ClassData.print()
509 
510     // Gets the type of processed binary
511     private boolean isModuleUnit() {
512         return moduleData != null;
513     }
514 
515     private void printMemberDataList( List<? extends MemberData> list) throws IOException {
516         if( list != null ) {
517             int count = list.size();
518             if( count > 0 ) {
519                 for( int i=0; i < count; i++ ) {
520                     MemberData md = list.get(i);
521                     md.setIndent(Options.BODY_INDENT);
522                     if( i !=0 && md.getAnnotationsCount() > 0 )
523                         out.println();
524                     md.print();
525                 }
526                 out.println();
527             }
528         }
529     }
530 
531     private List<IOException> getIssues() {
532         return this.pool.pool.stream().
533                 filter(Objects::nonNull).
534                 filter(c->c.getIssue() != null).
535                 map(ConstantPool.Constant::getIssue).
536                 collect(Collectors.toList());
537     }
538 
539 }// end class ClassData