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.jasm; 24 25 import java.io.*; 26 import java.util.ArrayList; 27 import java.util.List; 28 29 import static org.openjdk.asmtools.jasm.Tables.*; 30 31 /** 32 * ClassData 33 * 34 * This is the main data structure for representing parsed class data. This structure 35 * renders directly to a class file. 36 * 37 */ 38 class ClassData extends MemberData { 39 40 /* ClassData Fields */ 41 CFVersion cfv; 42 ConstantPool.ConstCell me, father; 43 String myClassName; 44 AttrData sourceFileNameAttr; 45 ArrayList<Argument> interfaces; 46 ArrayList<FieldData> fields = new ArrayList<>(); 47 ArrayList<MethodData> methods = new ArrayList<>(); 48 DataVectorAttr<InnerClassData> innerClasses = null; 49 DataVectorAttr<BootstrapMethodData> bootstrapMethodsAttr = null; 50 51 // JEP 181 - NestHost, NestMembers attributes since class version 55.0 52 CPXAttr nestHostAttr; 53 NestMembersAttr nestMembersAttr; 54 55 // JEP 359 - Record attribute since class file 58.65535 56 private RecordData recordData; 57 58 // JEP 360 - PermittedSubclasses attribute since class file 59.65535 59 private PermittedSubclassesAttr permittedSubclassesAttr; 60 61 private PreloadAttr preloadAttr; 62 63 ModuleAttr moduleAttribute = null; 64 Environment env; 65 protected ConstantPool pool; 66 67 private static final String DEFAULT_EXTENSION = ".class"; 68 String fileExtension = DEFAULT_EXTENSION; 69 public CDOutputStream cdos; 70 71 /** 72 * Initializes the ClassData. 73 * 74 * @param me The constant pool reference to this class 75 * @param father The constant pool reference to the super class 76 * @param interfaces A list of interfaces that this class implements 77 */ 78 public final void init(int access, ConstantPool.ConstCell me, ConstantPool.ConstCell father, ArrayList<Argument> interfaces) { 79 this.access = access; 80 81 // normalize the modifiers to access flags 82 if (Modifiers.hasPseudoMod(access)) { 83 createPseudoMod(); 84 } 85 86 this.me = me; 87 if (father == null) { 88 father = pool.FindCellClassByName("java/lang/Object"); 89 } 90 this.father = father; 91 this.interfaces = interfaces; 92 // Set default class file version if it is not set. 93 cfv.initClassDefaults(); 94 } 95 96 public final void initAsModule() { 97 this.access = RuntimeConstants.ACC_MODULE; 98 // this_class" module-info 99 this.me = pool.FindCellClassByName("module-info"); 100 // super_class: zero 101 this.father = new ConstantPool.ConstCell(0); 102 cfv.initModuleDefaults(); 103 } 104 105 /** 106 * canonical default constructor 107 * 108 * @param env The error reporting environment. 109 * @param cfv The class file version that this class file supports. 110 */ 111 public ClassData(Environment env, CFVersion cfv) { 112 super(null, 0); // for a class, these get inited in the super - later. 113 cls = this; 114 115 this.env = env; 116 this.cfv = cfv; 117 118 pool = new ConstantPool(env); 119 cdos = new CDOutputStream(); 120 } 121 122 /** 123 * Predicate that describes if this class has an access flag indicating that it is an 124 * interface. 125 * 126 * @return True if the classes access flag indicates it is an interface. 127 */ 128 public final boolean isInterface() { 129 return Modifiers.isInterface(access); 130 } 131 132 /* 133 * After a constant pool has been explicitly declared, 134 * this method links the Constant_InvokeDynamic Constant_Dynamic 135 * constants with any bootstrap methods that they index in the 136 * Bootstrap Methods Attribute 137 */ 138 protected void relinkBootstrapMethods() { 139 if (bootstrapMethodsAttr == null) { 140 return; 141 } 142 143 env.traceln("relinkBootstrapMethods"); 144 145 for (ConstantPool.ConstCell cell : pool) { 146 ConstantPool.ConstValue ref = null; 147 if (cell != null) { 148 ref = cell.ref; 149 } 150 if (ref != null 151 && (ref.tag == ConstType.CONSTANT_INVOKEDYNAMIC || ref.tag == ConstType.CONSTANT_DYNAMIC)) { 152 // Find only the Constant 153 ConstantPool.ConstValue_IndyOrCondyPair refval = (ConstantPool.ConstValue_IndyOrCondyPair) ref; 154 BootstrapMethodData bsmdata = refval.bsmData; 155 // only care about BSM Data that were placeholders 156 if (bsmdata != null && bsmdata.isPlaceholder()) { 157 // find the real BSM Data at the index 158 int bsmindex = bsmdata.placeholder_index; 159 if (bsmindex < 0 || bsmindex > bootstrapMethodsAttr.size()) { 160 // bad BSM index -- 161 // give a warning, but place the index in the arg anyway 162 env.traceln("Warning: (ClassData.relinkBootstrapMethods()): Bad bootstrapMethods index: " + bsmindex); 163 // env.error("const.bsmindex", bsmindex); 164 bsmdata.arg = bsmindex; 165 } else { 166 167 BootstrapMethodData realbsmdata = bootstrapMethodsAttr.get(bsmindex); 168 // make the IndyPairs BSM Data point to the one from the attribute 169 refval.bsmData = realbsmdata; 170 } 171 } 172 } 173 } 174 } 175 176 protected void numberBootstrapMethods() { 177 env.traceln("Numbering Bootstrap Methods"); 178 if (bootstrapMethodsAttr == null) { 179 return; 180 } 181 182 int index = 0; 183 for (BootstrapMethodData data : bootstrapMethodsAttr) { 184 data.arg = index++; 185 } 186 } 187 188 // API 189 // Record 190 public RecordData setRecord(int where) { 191 if( recordAttributeExists() ) { 192 env.error(where, "warn.record.repeated"); 193 } 194 this.recordData = new RecordData(cls); 195 return this.recordData; 196 } 197 198 /** 199 * Rejects a record: removes the record attribute if there are no components 200 */ 201 public void rejectRecord() { 202 this.recordData = null; 203 } 204 205 // Field 206 public ConstantPool.ConstValue_Pair mkNape(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { 207 return new ConstantPool.ConstValue_Pair(ConstType.CONSTANT_NAMEANDTYPE, name, sig); 208 } 209 210 public ConstantPool.ConstValue_Pair mkNape(String name, String sig) { 211 return mkNape(pool.FindCellAsciz(name), pool.FindCellAsciz(sig)); 212 } 213 214 public FieldData addFieldIfAbsent(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { 215 ConstantPool.ConstValue_Pair nape = mkNape(name, sig); 216 env.traceln(" [ClassData.addFieldIfAbsent]: #" + nape.left.arg + ":#" + nape.right.arg); 217 FieldData fd = getField(nape); 218 if( fd == null ) { 219 env.traceln(" [ClassData.addFieldIfAbsent]: new field."); 220 fd = addField(access,nape); 221 } 222 return fd; 223 } 224 225 private FieldData getField(ConstantPool.ConstValue_Pair nape) { 226 for (FieldData fd : fields) { 227 if( fd.getNameDesc().equals(nape) ) { 228 return fd; 229 } 230 } 231 return null; 232 } 233 234 public FieldData addField(int access, ConstantPool.ConstValue_Pair nape) { 235 env.traceln(" [ClassData.addField]: #" + nape.left.arg + ":#" + nape.right.arg); 236 FieldData res = new FieldData(this, access, nape); 237 fields.add(res); 238 return res; 239 } 240 241 public FieldData addField(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { 242 return addField(access, mkNape(name, sig)); 243 } 244 245 public FieldData addField(int access, String name, String type) { 246 return addField(access, pool.FindCellAsciz(name), pool.FindCellAsciz(type)); 247 } 248 249 public ConstantPool.ConstCell LocalFieldRef(FieldData field) { 250 return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(field.getNameDesc())); 251 } 252 253 public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstValue nape) { 254 return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(nape)); 255 } 256 257 public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { 258 return LocalFieldRef(mkNape(name, sig)); 259 } 260 261 public ConstantPool.ConstCell LocalFieldRef(String name, String sig) { 262 return LocalFieldRef(pool.FindCellAsciz(name), pool.FindCellAsciz(sig)); 263 } 264 265 MethodData curMethod; 266 267 public MethodData StartMethod(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig, ArrayList exc_table) { 268 EndMethod(); 269 env.traceln(" [ClassData.StartMethod]: #" + name.arg + ":#" + sig.arg); 270 curMethod = new MethodData(this, access, name, sig, exc_table); 271 methods.add(curMethod); 272 return curMethod; 273 } 274 275 public void EndMethod() { 276 curMethod = null; 277 } 278 279 public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstValue nape) { 280 return pool.FindCell(ConstType.CONSTANT_METHOD, me, pool.FindCell(nape)); 281 } 282 283 public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { 284 return LocalMethodRef(mkNape(name, sig)); 285 } 286 287 void addLocVarData(int opc, Argument arg) { 288 } 289 290 public void addInnerClass(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell innerClass, ConstantPool.ConstCell outerClass) { 291 env.traceln("addInnerClass (with indexes: Name (" + name.toString() + "), Inner (" + innerClass.toString() + "), Outer (" + outerClass.toString() + ")."); 292 if (innerClasses == null) { 293 innerClasses = new DataVectorAttr<>(this, AttrTag.ATT_InnerClasses.parsekey()); 294 } 295 innerClasses.add(new InnerClassData(access, name, innerClass, outerClass)); 296 } 297 298 public void addBootstrapMethod(BootstrapMethodData bsmData) { 299 env.traceln("addBootstrapMethod"); 300 if (bootstrapMethodsAttr == null) { 301 bootstrapMethodsAttr = new DataVectorAttr<>(this, AttrTag.ATT_BootstrapMethods.parsekey()); 302 } 303 bootstrapMethodsAttr.add(bsmData); 304 } 305 306 public void addNestHost(ConstantPool.ConstCell hostClass) { 307 env.traceln("addNestHost"); 308 nestHostAttr = new CPXAttr(this, AttrTag.ATT_NestHost.parsekey(), hostClass); 309 } 310 311 public void addNestMembers(List<ConstantPool.ConstCell> classes) { 312 env.traceln("addNestMembers"); 313 nestMembersAttr = new NestMembersAttr(this, classes); 314 } 315 316 public void addPermittedSubclasses(List<ConstantPool.ConstCell> classes) { 317 env.traceln("addPermittedSubclasses"); 318 permittedSubclassesAttr = new PermittedSubclassesAttr(this, classes); 319 } 320 321 public void addPreloads(List<ConstantPool.ConstCell> classes) { 322 env.traceln("addPreloads"); 323 preloadAttr = new PreloadAttr(this, classes); 324 } 325 326 public void endClass() { 327 sourceFileNameAttr = new CPXAttr(this, 328 AttrTag.ATT_SourceFile.parsekey(), 329 pool.FindCellAsciz(env.getSimpleInputFileName())); 330 pool.NumberizePool(); 331 pool.CheckGlobals(); 332 numberBootstrapMethods(); 333 try { 334 me = pool.uncheckedGetCell(me.arg); 335 env.traceln("me=" + me); 336 ConstantPool.ConstValue_Cell me_value = (ConstantPool.ConstValue_Cell) me.ref; 337 ConstantPool.ConstCell ascicell = me_value.cell; 338 env.traceln("ascicell=" + ascicell); 339 ConstantPool.ConstValue_String me_str = (ConstantPool.ConstValue_String) ascicell.ref; 340 myClassName = me_str.value; 341 env.traceln("-------------------"); 342 env.traceln("-- Constant Pool --"); 343 env.traceln("-------------------"); 344 pool.printPool(); 345 env.traceln("-------------------"); 346 env.traceln(" "); 347 env.traceln(" "); 348 env.traceln("-------------------"); 349 env.traceln("-- Inner Classes --"); 350 env.traceln("-------------------"); 351 printInnerClasses(); 352 353 } catch (Throwable e) { 354 env.traceln("check name:" + e); 355 env.error("no.classname"); 356 e.printStackTrace(); 357 } 358 } 359 360 public void endModule(ModuleAttr moduleAttr) { 361 moduleAttribute = moduleAttr.build(); 362 pool.NumberizePool(); 363 pool.CheckGlobals(); 364 myClassName = "module-info"; 365 } 366 367 private void printInnerClasses() { 368 if (innerClasses != null) { 369 int i = 1; 370 for (InnerClassData entry : innerClasses) { 371 env.trace(" InnerClass[" + i + "]: (" + Modifiers.toString(entry.access, CF_Context.CTX_INNERCLASS) + "]), "); 372 env.trace("Name: " + entry.name.toString() + " "); 373 env.trace("IC_info: " + entry.innerClass.toString() + " "); 374 env.trace("OC_info: " + entry.outerClass.toString() + " "); 375 env.traceln(" "); 376 i += 1; 377 } 378 } else { 379 env.traceln("<< NO INNER CLASSES >>"); 380 } 381 382 } 383 384 public void write(CheckedDataOutputStream out) throws IOException { 385 386 // Write the header 387 out.writeInt(JAVA_MAGIC); 388 out.writeShort(cfv.minor_version()); 389 out.writeShort(cfv.major_version()); 390 391 pool.write(out); 392 out.writeShort(access); // & MM_CLASS; // Q 393 out.writeShort(me.arg); 394 out.writeShort(father.arg); 395 396 // Write the interface names 397 if (interfaces != null) { 398 out.writeShort(interfaces.size()); 399 for (Argument intf : interfaces) { 400 out.writeShort(intf.arg); 401 } 402 } else { 403 out.writeShort(0); 404 } 405 406 // Write the fields 407 if (fields != null) { 408 out.writeShort(fields.size()); 409 for (FieldData field : fields) { 410 field.write(out); 411 } 412 } else { 413 out.writeShort(0); 414 } 415 416 // Write the methods 417 if (methods != null) { 418 out.writeShort(methods.size()); 419 for (MethodData method : methods) { 420 method.write(out); 421 } 422 } else { 423 out.writeShort(0); 424 } 425 426 // Write the attributes 427 DataVector attrs = getAttrVector(); 428 attrs.write(out); 429 } // end ClassData.write() 430 431 @Override 432 protected DataVector getAttrVector() { 433 DataVector attrs = new DataVector(); 434 if( moduleAttribute != null ) { 435 if (annotAttrVis != null) 436 attrs.add(annotAttrVis); 437 if (annotAttrInv != null) 438 attrs.add(annotAttrInv); 439 attrs.add(moduleAttribute); 440 } else { 441 attrs.add(sourceFileNameAttr); 442 // JEP 359 since class file 58.65535 443 if( recordData != null ) { 444 attrs.add(recordData); 445 } 446 if (innerClasses != null) 447 attrs.add(innerClasses); 448 if (syntheticAttr != null) 449 attrs.add(syntheticAttr); 450 if (deprecatedAttr != null) 451 attrs.add(deprecatedAttr); 452 if (annotAttrVis != null) 453 attrs.add(annotAttrVis); 454 if (annotAttrInv != null) 455 attrs.add(annotAttrInv); 456 if (type_annotAttrVis != null) 457 attrs.add(type_annotAttrVis); 458 if (type_annotAttrInv != null) 459 attrs.add(type_annotAttrInv); 460 if (bootstrapMethodsAttr != null) 461 attrs.add(bootstrapMethodsAttr); 462 // since class version 55.0 463 if(nestHostAttributeExists()) 464 attrs.add(nestHostAttr); 465 if(nestMembersAttributesExist()) 466 attrs.add(nestMembersAttr); 467 // since class version 59.65535 (JEP 360) 468 if ( permittedSubclassesAttributesExist() ) 469 attrs.add(permittedSubclassesAttr); 470 if (preloadAttributeExists()) 471 attrs.add(preloadAttr); 472 } 473 return attrs; 474 } 475 476 static char fileSeparator; //=System.getProperty("file.separator"); 477 478 /** 479 * Writes to the directory passed with -d option 480 */ 481 public void write(File destdir) throws IOException { 482 File outfile; 483 if (destdir == null) { 484 int startofname = myClassName.lastIndexOf("/"); 485 if (startofname != -1) { 486 myClassName = myClassName.substring(startofname + 1); 487 } 488 outfile = new File(myClassName + fileExtension); 489 } else { 490 env.traceln("writing -d " + destdir.getPath()); 491 if (fileSeparator == 0) { 492 fileSeparator = System.getProperty("file.separator").charAt(0); 493 } 494 if (fileSeparator != '/') { 495 myClassName = myClassName.replace('/', fileSeparator); 496 } 497 outfile = new File(destdir, myClassName + fileExtension); 498 File outdir = new File(outfile.getParent()); 499 if (!outdir.exists() && !outdir.mkdirs()) { 500 env.error("cannot.write", outdir.getPath()); 501 return; 502 } 503 } 504 505 DataOutputStream dos = new DataOutputStream( 506 new BufferedOutputStream(new FileOutputStream(outfile))); 507 cdos.setDataOutputStream(dos); 508 try { 509 write(cdos); 510 } finally { 511 dos.close(); 512 } 513 } // end write() 514 515 public void setByteLimit(int bytelimit) { 516 cdos.enable(); 517 cdos.setLimit(bytelimit); 518 } 519 520 public boolean nestHostAttributeExists() { 521 return nestHostAttr != null; 522 } 523 524 public boolean nestMembersAttributesExist() { return nestMembersAttr != null; } 525 526 public boolean permittedSubclassesAttributesExist() { return permittedSubclassesAttr != null; } 527 528 public boolean recordAttributeExists() { return recordData != null; } 529 530 public boolean preloadAttributeExists() { return preloadAttr != null; } 531 532 /** 533 * This is a wrapper for DataOutputStream, used for debugging purposes. it allows 534 * writing the byte-stream of a class up to a given byte number. 535 */ 536 static private class CDOutputStream implements CheckedDataOutputStream { 537 538 private int bytelimit; 539 private DataOutputStream dos; 540 public boolean enabled = false; 541 542 public CDOutputStream() { 543 dos = null; 544 } 545 546 public CDOutputStream(OutputStream out) { 547 setOutputStream(out); 548 } 549 550 public final void setOutputStream(OutputStream out) { 551 dos = new DataOutputStream(out); 552 } 553 554 public void setDataOutputStream(DataOutputStream dos) { 555 this.dos = dos; 556 } 557 558 public void setLimit(int lim) { 559 bytelimit = lim; 560 } 561 562 public void enable() { 563 enabled = true; 564 } 565 566 private synchronized void check(String loc) throws IOException { 567 if (enabled && dos.size() >= bytelimit) { 568 throw new IOException(loc); 569 } 570 } 571 572 @Override 573 public synchronized void write(int b) throws IOException { 574 dos.write(b); 575 check("Writing byte: " + b); 576 } 577 578 @Override 579 public synchronized void write(byte b[], int off, int len) throws IOException { 580 dos.write(b, off, len); 581 check("Writing byte-array: " + b); 582 } 583 584 @Override 585 public final void writeBoolean(boolean v) throws IOException { 586 dos.writeBoolean(v); 587 check("Writing writeBoolean: " + (v ? "true" : "false")); 588 } 589 590 @Override 591 public final void writeByte(int v) throws IOException { 592 dos.writeByte(v); 593 check("Writing writeByte: " + v); 594 } 595 596 @Override 597 public void writeShort(int v) throws IOException { 598 dos.writeShort(v); 599 check("Writing writeShort: " + v); 600 } 601 602 @Override 603 public void writeChar(int v) throws IOException { 604 dos.writeChar(v); 605 check("Writing writeChar: " + v); 606 } 607 608 @Override 609 public void writeInt(int v) throws IOException { 610 dos.writeInt(v); 611 check("Writing writeInt: " + v); 612 } 613 614 @Override 615 public void writeLong(long v) throws IOException { 616 dos.writeLong(v); 617 check("Writing writeLong: " + v); 618 } 619 620 @Override 621 public void writeFloat(float v) throws IOException { 622 dos.writeFloat(v); 623 check("Writing writeFloat: " + v); 624 } 625 626 @Override 627 public void writeDouble(double v) throws IOException { 628 dos.writeDouble(v); 629 check("Writing writeDouble: " + v); 630 } 631 632 @Override 633 public void writeBytes(String s) throws IOException { 634 dos.writeBytes(s); 635 check("Writing writeBytes: " + s); 636 } 637 638 @Override 639 public void writeChars(String s) throws IOException { 640 dos.writeChars(s); 641 check("Writing writeChars: " + s); 642 } 643 644 @Override 645 public void writeUTF(String s) throws IOException { 646 dos.writeUTF(s); 647 check("Writing writeUTF: " + s); 648 } 649 } 650 }// end class ClassData