1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 16 package javassist.bytecode; 17 18 import java.io.OutputStream; 19 import java.io.DataOutputStream; 20 import java.io.ByteArrayOutputStream; 21 import java.io.IOException; 22 23 /** 24 * A quick class-file writer. This is useful when a generated 25 * class file is simple and the code generation should be fast. 26 * 27 * <p>Example: 28 * 29 * <blockquote><pre> 30 * ClassFileWriter cfw = new ClassFileWriter(ClassFile.JAVA_4, 0); 31 * ConstPoolWriter cpw = cfw.getConstPool(); 32 * 33 * FieldWriter fw = cfw.getFieldWriter(); 34 * fw.add(AccessFlag.PUBLIC, "value", "I", null); 35 * fw.add(AccessFlag.PUBLIC, "value2", "J", null); 36 * 37 * int thisClass = cpw.addClassInfo("sample/Test"); 38 * int superClass = cpw.addClassInfo("java/lang/Object"); 39 * 40 * MethodWriter mw = cfw.getMethodWriter(); 41 * 42 * mw.begin(AccessFlag.PUBLIC, MethodInfo.nameInit, "()V", null, null); 43 * mw.add(Opcode.ALOAD_0); 44 * mw.add(Opcode.INVOKESPECIAL); 45 * int signature = cpw.addNameAndTypeInfo(MethodInfo.nameInit, "()V"); 46 * mw.add16(cpw.addMethodrefInfo(superClass, signature)); 47 * mw.add(Opcode.RETURN); 48 * mw.codeEnd(1, 1); 49 * mw.end(null, null); 50 * 51 * mw.begin(AccessFlag.PUBLIC, "one", "()I", null, null); 52 * mw.add(Opcode.ICONST_1); 53 * mw.add(Opcode.IRETURN); 54 * mw.codeEnd(1, 1); 55 * mw.end(null, null); 56 * 57 * byte[] classfile = cfw.end(AccessFlag.PUBLIC, thisClass, superClass, 58 * null, null); 59 * </pre></blockquote> 60 * 61 * <p>The code above generates the following class: 62 * 63 * <blockquote><pre> 64 * package sample; 65 * public class Test { 66 * public int value; 67 * public long value2; 68 * public Test() { super(); } 69 * public one() { return 1; } 70 * } 71 * </pre></blockquote> 72 * 73 * @since 3.13 74 */ 75 public class ClassFileWriter { 76 private ByteStream output; 77 private ConstPoolWriter constPool; 78 private FieldWriter fields; 79 private MethodWriter methods; 80 int thisClass, superClass; 81 82 /** 83 * Constructs a class file writer. 84 * 85 * @param major the major version ({@link ClassFile#JAVA_4}, {@link ClassFile#JAVA_5}, ...). 86 * @param minor the minor version (0 for JDK 1.3 and later). 87 */ ClassFileWriter(int major, int minor)88 public ClassFileWriter(int major, int minor) { 89 output = new ByteStream(512); 90 output.writeInt(0xCAFEBABE); // magic 91 output.writeShort(minor); 92 output.writeShort(major); 93 constPool = new ConstPoolWriter(output); 94 fields = new FieldWriter(constPool); 95 methods = new MethodWriter(constPool); 96 97 } 98 99 /** 100 * Returns a constant pool. 101 */ getConstPool()102 public ConstPoolWriter getConstPool() { return constPool; } 103 104 /** 105 * Returns a filed writer. 106 */ getFieldWriter()107 public FieldWriter getFieldWriter() { return fields; } 108 109 /** 110 * Returns a method writer. 111 */ getMethodWriter()112 public MethodWriter getMethodWriter() { return methods; } 113 114 /** 115 * Ends writing and returns the contents of the class file. 116 * 117 * @param accessFlags access flags. 118 * @param thisClass this class. an index indicating its <code>CONSTANT_Class_info</code>. 119 * @param superClass super class. an index indicating its <code>CONSTANT_Class_info</code>. 120 * @param interfaces implemented interfaces. 121 * index numbers indicating their <code>ClassInfo</code>. 122 * It may be null. 123 * @param aw attributes of the class file. May be null. 124 * 125 * @see AccessFlag 126 */ end(int accessFlags, int thisClass, int superClass, int[] interfaces, AttributeWriter aw)127 public byte[] end(int accessFlags, int thisClass, int superClass, 128 int[] interfaces, AttributeWriter aw) { 129 constPool.end(); 130 output.writeShort(accessFlags); 131 output.writeShort(thisClass); 132 output.writeShort(superClass); 133 if (interfaces == null) 134 output.writeShort(0); 135 else { 136 int n = interfaces.length; 137 output.writeShort(n); 138 for (int i = 0; i < n; i++) 139 output.writeShort(interfaces[i]); 140 } 141 142 output.enlarge(fields.dataSize() + methods.dataSize() + 6); 143 try { 144 output.writeShort(fields.size()); 145 fields.write(output); 146 147 output.writeShort(methods.size()); 148 methods.write(output); 149 } 150 catch (IOException e) {} 151 152 writeAttribute(output, aw, 0); 153 return output.toByteArray(); 154 } 155 156 /** 157 * Ends writing and writes the contents of the class file into the 158 * given output stream. 159 * 160 * @param accessFlags access flags. 161 * @param thisClass this class. an index indicating its <code>CONSTANT_Class_info</code>. 162 * @param superClass super class. an index indicating its <code>CONSTANT_Class_info</code>. 163 * @param interfaces implemented interfaces. 164 * index numbers indicating their <code>CONSTATNT_Class_info</code>. 165 * It may be null. 166 * @param aw attributes of the class file. May be null. 167 * 168 * @see AccessFlag 169 */ end(DataOutputStream out, int accessFlags, int thisClass, int superClass, int[] interfaces, AttributeWriter aw)170 public void end(DataOutputStream out, 171 int accessFlags, int thisClass, int superClass, 172 int[] interfaces, AttributeWriter aw) 173 throws IOException 174 { 175 constPool.end(); 176 output.writeTo(out); 177 out.writeShort(accessFlags); 178 out.writeShort(thisClass); 179 out.writeShort(superClass); 180 if (interfaces == null) 181 out.writeShort(0); 182 else { 183 int n = interfaces.length; 184 out.writeShort(n); 185 for (int i = 0; i < n; i++) 186 out.writeShort(interfaces[i]); 187 } 188 189 out.writeShort(fields.size()); 190 fields.write(out); 191 192 out.writeShort(methods.size()); 193 methods.write(out); 194 if (aw == null) 195 out.writeShort(0); 196 else { 197 out.writeShort(aw.size()); 198 aw.write(out); 199 } 200 } 201 202 /** 203 * This writes attributes. 204 * 205 * <p>For example, the following object writes a synthetic attribute: 206 * 207 * <pre> 208 * ConstPoolWriter cpw = ...; 209 * final int tag = cpw.addUtf8Info("Synthetic"); 210 * AttributeWriter aw = new AttributeWriter() { 211 * public int size() { 212 * return 1; 213 * } 214 * public void write(DataOutputStream out) throws java.io.IOException { 215 * out.writeShort(tag); 216 * out.writeInt(0); 217 * } 218 * }; 219 * </pre> 220 */ 221 public static interface AttributeWriter { 222 /** 223 * Returns the number of attributes that this writer will 224 * write. 225 */ size()226 public int size(); 227 228 /** 229 * Writes all the contents of the attributes. The binary representation 230 * of the contents is an array of <code>attribute_info</code>. 231 */ write(DataOutputStream out)232 public void write(DataOutputStream out) throws IOException; 233 } 234 writeAttribute(ByteStream bs, AttributeWriter aw, int attrCount)235 static void writeAttribute(ByteStream bs, AttributeWriter aw, int attrCount) { 236 if (aw == null) { 237 bs.writeShort(attrCount); 238 return; 239 } 240 241 bs.writeShort(aw.size() + attrCount); 242 DataOutputStream dos = new DataOutputStream(bs); 243 try { 244 aw.write(dos); 245 dos.flush(); 246 } 247 catch (IOException e) {} 248 } 249 250 /** 251 * Field. 252 */ 253 public static final class FieldWriter { 254 protected ByteStream output; 255 protected ConstPoolWriter constPool; 256 private int fieldCount; 257 FieldWriter(ConstPoolWriter cp)258 FieldWriter(ConstPoolWriter cp) { 259 output = new ByteStream(128); 260 constPool = cp; 261 fieldCount = 0; 262 } 263 264 /** 265 * Adds a new field. 266 * 267 * @param accessFlags access flags. 268 * @param name the field name. 269 * @param descriptor the field type. 270 * @param aw the attributes of the field. may be null. 271 * @see AccessFlag 272 */ add(int accessFlags, String name, String descriptor, AttributeWriter aw)273 public void add(int accessFlags, String name, String descriptor, AttributeWriter aw) { 274 int nameIndex = constPool.addUtf8Info(name); 275 int descIndex = constPool.addUtf8Info(descriptor); 276 add(accessFlags, nameIndex, descIndex, aw); 277 } 278 279 /** 280 * Adds a new field. 281 * 282 * @param accessFlags access flags. 283 * @param name the field name. an index indicating its <code>CONSTANT_Utf8_info</code>. 284 * @param descriptor the field type. an index indicating its <code>CONSTANT_Utf8_info</code>. 285 * @param aw the attributes of the field. may be null. 286 * @see AccessFlag 287 */ add(int accessFlags, int name, int descriptor, AttributeWriter aw)288 public void add(int accessFlags, int name, int descriptor, AttributeWriter aw) { 289 ++fieldCount; 290 output.writeShort(accessFlags); 291 output.writeShort(name); 292 output.writeShort(descriptor); 293 writeAttribute(output, aw, 0); 294 } 295 size()296 int size() { return fieldCount; } 297 dataSize()298 int dataSize() { return output.size(); } 299 300 /** 301 * Writes the added fields. 302 */ write(OutputStream out)303 void write(OutputStream out) throws IOException { 304 output.writeTo(out); 305 } 306 } 307 308 /** 309 * Method. 310 */ 311 public static final class MethodWriter { 312 protected ByteStream output; 313 protected ConstPoolWriter constPool; 314 private int methodCount; 315 protected int codeIndex; 316 protected int throwsIndex; 317 protected int stackIndex; 318 319 private int startPos; 320 private boolean isAbstract; 321 private int catchPos; 322 private int catchCount; 323 MethodWriter(ConstPoolWriter cp)324 MethodWriter(ConstPoolWriter cp) { 325 output = new ByteStream(256); 326 constPool = cp; 327 methodCount = 0; 328 codeIndex = 0; 329 throwsIndex = 0; 330 stackIndex = 0; 331 } 332 333 /** 334 * Starts Adding a new method. 335 * 336 * @param accessFlags access flags. 337 * @param name the method name. 338 * @param descriptor the method signature. 339 * @param exceptions throws clause. It may be null. 340 * The class names must be the JVM-internal 341 * representations like <code>java/lang/Exception</code>. 342 * @param aw attributes to the <code>Method_info</code>. 343 */ begin(int accessFlags, String name, String descriptor, String[] exceptions, AttributeWriter aw)344 public void begin(int accessFlags, String name, String descriptor, 345 String[] exceptions, AttributeWriter aw) { 346 int nameIndex = constPool.addUtf8Info(name); 347 int descIndex = constPool.addUtf8Info(descriptor); 348 int[] intfs; 349 if (exceptions == null) 350 intfs = null; 351 else 352 intfs = constPool.addClassInfo(exceptions); 353 354 begin(accessFlags, nameIndex, descIndex, intfs, aw); 355 } 356 357 /** 358 * Starts adding a new method. 359 * 360 * @param accessFlags access flags. 361 * @param name the method name. an index indicating its <code>CONSTANT_Utf8_info</code>. 362 * @param descriptor the field type. an index indicating its <code>CONSTANT_Utf8_info</code>. 363 * @param exceptions throws clause. indexes indicating <code>CONSTANT_Class_info</code>s. 364 * It may be null. 365 * @param aw attributes to the <code>Method_info</code>. 366 */ begin(int accessFlags, int name, int descriptor, int[] exceptions, AttributeWriter aw)367 public void begin(int accessFlags, int name, int descriptor, int[] exceptions, AttributeWriter aw) { 368 ++methodCount; 369 output.writeShort(accessFlags); 370 output.writeShort(name); 371 output.writeShort(descriptor); 372 isAbstract = (accessFlags & AccessFlag.ABSTRACT) != 0; 373 374 int attrCount = isAbstract ? 0 : 1; 375 if (exceptions != null) 376 ++attrCount; 377 378 writeAttribute(output, aw, attrCount); 379 380 if (exceptions != null) 381 writeThrows(exceptions); 382 383 if (!isAbstract) { 384 if (codeIndex == 0) 385 codeIndex = constPool.addUtf8Info(CodeAttribute.tag); 386 387 startPos = output.getPos(); 388 output.writeShort(codeIndex); 389 output.writeBlank(12); // attribute_length, maxStack, maxLocals, code_lenth 390 } 391 392 catchPos = -1; 393 catchCount = 0; 394 } 395 writeThrows(int[] exceptions)396 private void writeThrows(int[] exceptions) { 397 if (throwsIndex == 0) 398 throwsIndex = constPool.addUtf8Info(ExceptionsAttribute.tag); 399 400 output.writeShort(throwsIndex); 401 output.writeInt(exceptions.length * 2 + 2); 402 output.writeShort(exceptions.length); 403 for (int i = 0; i < exceptions.length; i++) 404 output.writeShort(exceptions[i]); 405 } 406 407 /** 408 * Appends an 8bit value of bytecode. 409 * 410 * @see Opcode 411 */ add(int b)412 public void add(int b) { 413 output.write(b); 414 } 415 416 /** 417 * Appends a 16bit value of bytecode. 418 */ add16(int b)419 public void add16(int b) { 420 output.writeShort(b); 421 } 422 423 /** 424 * Appends a 32bit value of bytecode. 425 */ add32(int b)426 public void add32(int b) { 427 output.writeInt(b); 428 } 429 430 /** 431 * Appends a invokevirtual, inovkespecial, or invokestatic bytecode. 432 * 433 * @see Opcode 434 */ addInvoke(int opcode, String targetClass, String methodName, String descriptor)435 public void addInvoke(int opcode, String targetClass, String methodName, 436 String descriptor) { 437 int target = constPool.addClassInfo(targetClass); 438 int nt = constPool.addNameAndTypeInfo(methodName, descriptor); 439 int method = constPool.addMethodrefInfo(target, nt); 440 add(opcode); 441 add16(method); 442 } 443 444 /** 445 * Ends appending bytecode. 446 */ codeEnd(int maxStack, int maxLocals)447 public void codeEnd(int maxStack, int maxLocals) { 448 if (!isAbstract) { 449 output.writeShort(startPos + 6, maxStack); 450 output.writeShort(startPos + 8, maxLocals); 451 output.writeInt(startPos + 10, output.getPos() - startPos - 14); // code_length 452 catchPos = output.getPos(); 453 catchCount = 0; 454 output.writeShort(0); // number of catch clauses 455 } 456 } 457 458 /** 459 * Appends an <code>exception_table</code> entry to the 460 * <code>Code_attribute</code>. This method is available 461 * only after the <code>codeEnd</code> method is called. 462 * 463 * @param catchType an index indicating a <code>CONSTANT_Class_info</code>. 464 */ addCatch(int startPc, int endPc, int handlerPc, int catchType)465 public void addCatch(int startPc, int endPc, int handlerPc, int catchType) { 466 ++catchCount; 467 output.writeShort(startPc); 468 output.writeShort(endPc); 469 output.writeShort(handlerPc); 470 output.writeShort(catchType); 471 } 472 473 /** 474 * Ends adding a new method. The <code>add</code> method must be 475 * called before the <code>end</code> method is called. 476 * 477 * @param smap a stack map table. may be null. 478 * @param aw attributes to the <code>Code_attribute</code>. 479 * may be null. 480 */ end(StackMapTable.Writer smap, AttributeWriter aw)481 public void end(StackMapTable.Writer smap, AttributeWriter aw) { 482 if (isAbstract) 483 return; 484 485 // exception_table_length 486 output.writeShort(catchPos, catchCount); 487 488 int attrCount = smap == null ? 0 : 1; 489 writeAttribute(output, aw, attrCount); 490 491 if (smap != null) { 492 if (stackIndex == 0) 493 stackIndex = constPool.addUtf8Info(StackMapTable.tag); 494 495 output.writeShort(stackIndex); 496 byte[] data = smap.toByteArray(); 497 output.writeInt(data.length); 498 output.write(data); 499 } 500 501 // Code attribute_length 502 output.writeInt(startPos + 2, output.getPos() - startPos - 6); 503 } 504 size()505 int size() { return methodCount; } 506 dataSize()507 int dataSize() { return output.size(); } 508 509 /** 510 * Writes the added methods. 511 */ write(OutputStream out)512 void write(OutputStream out) throws IOException { 513 output.writeTo(out); 514 } 515 } 516 517 /** 518 * Constant Pool. 519 */ 520 public static final class ConstPoolWriter { 521 ByteStream output; 522 protected int startPos; 523 protected int num; 524 ConstPoolWriter(ByteStream out)525 ConstPoolWriter(ByteStream out) { 526 output = out; 527 startPos = out.getPos(); 528 num = 1; 529 output.writeShort(1); // number of entries 530 } 531 532 /** 533 * Makes <code>CONSTANT_Class_info</code> objects for each class name. 534 * 535 * @return an array of indexes indicating <code>CONSTANT_Class_info</code>s. 536 */ addClassInfo(String[] classNames)537 public int[] addClassInfo(String[] classNames) { 538 int n = classNames.length; 539 int[] result = new int[n]; 540 for (int i = 0; i < n; i++) 541 result[i] = addClassInfo(classNames[i]); 542 543 return result; 544 } 545 546 /** 547 * Adds a new <code>CONSTANT_Class_info</code> structure. 548 * 549 * <p>This also adds a <code>CONSTANT_Utf8_info</code> structure 550 * for storing the class name. 551 * 552 * @param jvmname the JVM-internal representation of a class name. 553 * e.g. <code>java/lang/Object</code>. 554 * @return the index of the added entry. 555 */ addClassInfo(String jvmname)556 public int addClassInfo(String jvmname) { 557 int utf8 = addUtf8Info(jvmname); 558 output.write(ClassInfo.tag); 559 output.writeShort(utf8); 560 return num++; 561 } 562 563 /** 564 * Adds a new <code>CONSTANT_Class_info</code> structure. 565 * 566 * @param name <code>name_index</code> 567 * @return the index of the added entry. 568 */ addClassInfo(int name)569 public int addClassInfo(int name) { 570 output.write(ClassInfo.tag); 571 output.writeShort(name); 572 return num++; 573 } 574 575 /** 576 * Adds a new <code>CONSTANT_NameAndType_info</code> structure. 577 * 578 * @param name <code>name_index</code> 579 * @param type <code>descriptor_index</code> 580 * @return the index of the added entry. 581 */ addNameAndTypeInfo(String name, String type)582 public int addNameAndTypeInfo(String name, String type) { 583 return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type)); 584 } 585 586 /** 587 * Adds a new <code>CONSTANT_NameAndType_info</code> structure. 588 * 589 * @param name <code>name_index</code> 590 * @param type <code>descriptor_index</code> 591 * @return the index of the added entry. 592 */ addNameAndTypeInfo(int name, int type)593 public int addNameAndTypeInfo(int name, int type) { 594 output.write(NameAndTypeInfo.tag); 595 output.writeShort(name); 596 output.writeShort(type); 597 return num++; 598 } 599 600 /** 601 * Adds a new <code>CONSTANT_Fieldref_info</code> structure. 602 * 603 * @param classInfo <code>class_index</code> 604 * @param nameAndTypeInfo <code>name_and_type_index</code>. 605 * @return the index of the added entry. 606 */ addFieldrefInfo(int classInfo, int nameAndTypeInfo)607 public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) { 608 output.write(FieldrefInfo.tag); 609 output.writeShort(classInfo); 610 output.writeShort(nameAndTypeInfo); 611 return num++; 612 } 613 614 /** 615 * Adds a new <code>CONSTANT_Methodref_info</code> structure. 616 * 617 * @param classInfo <code>class_index</code> 618 * @param nameAndTypeInfo <code>name_and_type_index</code>. 619 * @return the index of the added entry. 620 */ addMethodrefInfo(int classInfo, int nameAndTypeInfo)621 public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) { 622 output.write(MethodrefInfo.tag); 623 output.writeShort(classInfo); 624 output.writeShort(nameAndTypeInfo); 625 return num++; 626 } 627 628 /** 629 * Adds a new <code>CONSTANT_InterfaceMethodref_info</code> 630 * structure. 631 * 632 * @param classInfo <code>class_index</code> 633 * @param nameAndTypeInfo <code>name_and_type_index</code>. 634 * @return the index of the added entry. 635 */ addInterfaceMethodrefInfo(int classInfo, int nameAndTypeInfo)636 public int addInterfaceMethodrefInfo(int classInfo, 637 int nameAndTypeInfo) { 638 output.write(InterfaceMethodrefInfo.tag); 639 output.writeShort(classInfo); 640 output.writeShort(nameAndTypeInfo); 641 return num++; 642 } 643 644 /** 645 * Adds a new <code>CONSTANT_String_info</code> 646 * structure. 647 * 648 * <p>This also adds a new <code>CONSTANT_Utf8_info</code> 649 * structure. 650 * 651 * @return the index of the added entry. 652 */ addStringInfo(String str)653 public int addStringInfo(String str) { 654 int utf8 = addUtf8Info(str); 655 output.write(StringInfo.tag); 656 output.writeShort(utf8); 657 return num++; 658 } 659 660 /** 661 * Adds a new <code>CONSTANT_Integer_info</code> 662 * structure. 663 * 664 * @return the index of the added entry. 665 */ addIntegerInfo(int i)666 public int addIntegerInfo(int i) { 667 output.write(IntegerInfo.tag); 668 output.writeInt(i); 669 return num++; 670 } 671 672 /** 673 * Adds a new <code>CONSTANT_Float_info</code> 674 * structure. 675 * 676 * @return the index of the added entry. 677 */ addFloatInfo(float f)678 public int addFloatInfo(float f) { 679 output.write(FloatInfo.tag); 680 output.writeFloat(f); 681 return num++; 682 } 683 684 /** 685 * Adds a new <code>CONSTANT_Long_info</code> 686 * structure. 687 * 688 * @return the index of the added entry. 689 */ addLongInfo(long l)690 public int addLongInfo(long l) { 691 output.write(LongInfo.tag); 692 output.writeLong(l); 693 int n = num; 694 num += 2; 695 return n; 696 } 697 698 /** 699 * Adds a new <code>CONSTANT_Double_info</code> 700 * structure. 701 * 702 * @return the index of the added entry. 703 */ addDoubleInfo(double d)704 public int addDoubleInfo(double d) { 705 output.write(DoubleInfo.tag); 706 output.writeDouble(d); 707 int n = num; 708 num += 2; 709 return n; 710 } 711 712 /** 713 * Adds a new <code>CONSTANT_Utf8_info</code> 714 * structure. 715 * 716 * @return the index of the added entry. 717 */ addUtf8Info(String utf8)718 public int addUtf8Info(String utf8) { 719 output.write(Utf8Info.tag); 720 output.writeUTF(utf8); 721 return num++; 722 } 723 724 /** 725 * Writes the contents of this class pool. 726 */ end()727 void end() { 728 output.writeShort(startPos, num); 729 } 730 } 731 } 732