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