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