1 /* --- Copyright Jonathan Meyer 1997. All rights reserved. ----------------- 2 > File: jasmin/src/jasmin/ClassFile.java 3 > Purpose: Uses a parser and the JAS package to create Java class files 4 > Author: Jonathan Meyer, 10 July 1996 5 */ 6 7 package jasmin; 8 9 10 /* 11 * This class is a bit monolithic, and should probably be converted into 12 * several smaller classes. However, for this specific application, 13 * its acceptable. 14 * 15 */ 16 17 import jas.*; 18 import java.io.*; 19 import java.util.*; 20 21 /** 22 * A ClassFile object is used to represent the binary data that makes up a 23 * Java class file - it also serves as the public 24 * API to the Jasmin assembler, though users should beware: the API 25 * is likely to change in future versions (though its such a small API 26 * at the moment that changes are likely to have only a small impact).<p> 27 * 28 * To assemble a file, you first construct a jasmin.ClassFile object, then 29 * call readJasmin() to read in the contents of a Jasmin assembly file, then 30 * call write() to write out the binary representation of the class file.<p> 31 * 32 * There are a few other utility methods as well. See Main.java for an example 33 * which uses all of the public methods provided in ClassFile. 34 * 35 * @author Jonathan Meyer 36 * @version 1.05, 8 Feb 1997 37 */ 38 public class ClassFile { 39 40 private static final boolean PARSER_DEBUG = false; 41 42 // state info for the class being built 43 String filename; 44 ClassEnv class_env; 45 String class_name; 46 String source_name; 47 Scanner scanner; 48 49 // state info for the current method being defined 50 ExceptAttr except_attr; 51 Catchtable catch_table; 52 LocalVarTableAttr var_table; 53 LocalVarTypeTableAttr vtype_table; 54 LineTableAttr line_table; 55 CodeAttr code; 56 Method cur_method; 57 Var cur_field; 58 Hashtable labels; 59 StackMap stackmap; 60 VerifyFrame verifyframe; 61 Annotation cur_annotation; 62 63 int line_label_count, line_num, stack_map_label_count; 64 boolean auto_number, class_header; 65 Insn buffered_insn; 66 67 // state info for lookupswitch and tableswitch instructions 68 Vector switch_vec; 69 int low_value; 70 int high_value; 71 72 73 static final String BGN_METHOD = "jasmin_reserved_bgnmethod:"; 74 static final String END_METHOD = "jasmin_reserved_endmethod:"; 75 76 // number of errors reported in a file. 77 int errors; 78 79 // 80 // Error reporting method 81 // report_error(String msg)82 void report_error(String msg) { report_error(msg, false); } 83 report_error(String msg, boolean BadIntVal)84 void report_error(String msg, boolean BadIntVal) { 85 if(Main.DEBUG) 86 throw new RuntimeException(); 87 88 errors++; 89 90 // Print out filename/linenumber/message 91 System.err.print(filename + ":"); 92 if (scanner == null) 93 System.err.println(" " + msg + "."); 94 else { 95 String dia_line; 96 int dia_linnum, dia_charpos; 97 98 if (BadIntVal && scanner.char_num >= 0) { 99 dia_line = scanner.int_line; 100 dia_linnum = scanner.int_line_num; 101 dia_charpos = scanner.int_char_num; 102 } else { 103 dia_line = scanner.line.toString(); 104 dia_linnum = scanner.line_num; 105 dia_charpos = scanner.char_num; 106 } 107 System.err.println(dia_linnum + ": " + msg + "."); 108 if (scanner.char_num >= 0) { 109 System.err.println(dia_line); 110 111 // Print out where on the line the scanner got to 112 int i; 113 for (i = 0; i < dia_charpos; i++) { 114 if (dia_line.charAt(i) == '\t') { 115 System.err.print("\t"); 116 } else { 117 System.err.print(" "); 118 } 119 } 120 System.err.println("^"); 121 } 122 } 123 } 124 125 // 126 // called by the .source directive 127 // setSource(String name)128 void setSource(String name) { 129 source_name = name; 130 } 131 132 // 133 // called by the .bytecode directive 134 // setVersion(Number version)135 void setVersion(Number version) { 136 String str = version.toString(); 137 int idx = str.indexOf("."); 138 if(!(version instanceof Float) || (idx == -1)) 139 report_error("invalid bytecode version number : " + str); 140 class_env.setVersion(Short.parseShort(str.substring(0,idx)), 141 Short.parseShort(str.substring(idx+1,str.length()))); 142 } 143 144 // 145 // called by the .class directive 146 // setClass(String name, short acc)147 void setClass(String name, short acc) { 148 class_name = name; 149 class_env.setClass(new ClassCP(name)); 150 class_env.setClassAccess(acc); 151 class_header = true; 152 } 153 154 // 155 // called by the .super directive 156 // setSuperClass(String name)157 void setSuperClass(String name) { 158 class_env.setSuperClass(new ClassCP(name)); 159 } 160 161 // 162 // called by the .implements directive 163 // addInterface(String name)164 void addInterface(String name) { 165 class_env.addInterface(new ClassCP(name)); 166 } 167 168 169 // 170 // called by the .debug directive 171 // setSourceDebugExtension(String str)172 void setSourceDebugExtension(String str) { 173 class_env.setSourceDebugExtension(str); 174 } 175 176 177 // 178 // called by the .enclosing directive 179 // setEnclosingMethod(String str)180 void setEnclosingMethod(String str) { 181 try { 182 if(str.indexOf("(") != -1) { // full method description 183 String[] split = ScannerUtils.splitClassMethodSignature(str); 184 class_env.setEnclosingMethod(split[0], split[1], split[2]); 185 } 186 else // just a class name 187 class_env.setEnclosingMethod(str, null, null); 188 } catch(IllegalArgumentException e) { 189 report_error(e.toString()); 190 } 191 } 192 193 // parser diagnostics opened_annotation(String fld)194 private static void opened_annotation(String fld) throws jasError 195 { throw new jasError("Skipped .end annotation in " + fld); } 196 outside(String dir)197 private static void outside(String dir) throws jasError 198 { throw new jasError("illegal use of " +dir+ 199 " outside of method/field definition or class header"); } 200 201 // 202 // called at end of jasmin-header (resolve class variables) 203 // endHeader()204 public void endHeader() throws jasError 205 { 206 if(cur_annotation != null) 207 opened_annotation("class header"); 208 209 class_env.endHeader(); // resolve annotations 210 class_header = false; 211 } 212 213 // 214 // called by the .signature directive 215 // setSignature(String str)216 void setSignature(String str) throws jasError 217 { 218 SignatureAttr sig = new SignatureAttr(str); 219 if (cur_method != null) { 220 cur_method.setSignature(sig); 221 } else if(cur_field != null) { 222 cur_field.setSignature(sig); 223 } else if(class_header) { 224 class_env.setSignature(str); 225 } else { 226 outside(".signature"); 227 } 228 } 229 230 // 231 // called by the .deprecated directive 232 // 233 // setDeprecated()234 void setDeprecated() throws jasError 235 { 236 DeprecatedAttr depr = new DeprecatedAttr(); 237 if ( cur_method != null ) { 238 cur_method.setDeprecated(depr); 239 } else if(cur_field != null) { 240 cur_field.setDeprecated(depr); 241 } else if(class_header) { 242 class_env.setDeprecated(depr); 243 } else { 244 outside(".deprecated"); 245 } 246 } 247 248 // 249 // called by the .attribute directive 250 // addGenericAttr(String name, String file)251 void addGenericAttr(String name, String file) 252 throws IOException, jasError 253 { 254 GenericAttr gattr = new GenericAttr(name, file); 255 if (cur_method != null) { 256 flushInsnBuffer(); 257 if(code != null) code.addGenericAttr(gattr); 258 else cur_method.addGenericAttr(gattr); 259 } else if(cur_field != null) { 260 cur_field.addGenericAttr(gattr); 261 } else if (class_header) { 262 class_env.addGenericAttr(gattr); 263 } else { 264 outside(".attribute"); 265 } 266 } 267 268 // 269 // called by the .inner directive 270 // 271 // addInner(short iacc, String name, String inner, String outer)272 public void addInner(short iacc, String name, String inner, String outer) 273 { class_env.addInnerClass(iacc, name, inner, outer); } 274 275 // 276 // procedure group for annotation description 277 // annotation_internal()278 private static void annotation_internal() throws jasError 279 { throw new jasError("logic error in .annotation parsing"); } 280 addAnnotation()281 void addAnnotation() throws jasError 282 { 283 if(cur_method == null) Annotation.ParserError(); 284 cur_annotation = cur_method.addAnnotation(); 285 } 286 addAnnotation(boolean visible, String clname, int paramnum)287 void addAnnotation(boolean visible, String clname, int paramnum) 288 throws jasError 289 { 290 if(cur_method == null) Annotation.ParserError(); 291 cur_annotation = cur_method.addAnnotation(visible, clname, paramnum); 292 } 293 addAnnotation(boolean visible, String clname)294 void addAnnotation(boolean visible, String clname) throws jasError 295 { 296 if (cur_method != null) { 297 cur_annotation = cur_method.addAnnotation(visible, clname); 298 } else if(cur_field != null) { 299 cur_annotation = cur_field.addAnnotation(visible, clname); 300 } else if (class_header) { 301 cur_annotation = class_env.addAnnotation(visible, clname); 302 } else { 303 outside(".annotation"); 304 } 305 } 306 addAnnotationField(String name, String type, String add)307 void addAnnotationField(String name, String type, String add) 308 throws jasError 309 { 310 if(cur_annotation == null) annotation_internal(); 311 cur_annotation.addField(name, type, add); 312 } 313 addAnnotationValue(Object value)314 void addAnnotationValue(Object value) throws jasError 315 { 316 if(cur_annotation == null) annotation_internal(); 317 cur_annotation.addValue(value); 318 } 319 nestAnnotation()320 void nestAnnotation() throws jasError 321 { 322 if(cur_annotation == null) annotation_internal(); 323 cur_annotation = cur_annotation.nestAnnotation(); 324 } 325 endAnnotation()326 void endAnnotation() throws jasError 327 { 328 if(cur_annotation == null) 329 throw new jasError(".end annotation without .annotation"); 330 cur_annotation = cur_annotation.endAnnotation(); 331 } 332 333 // called by the .field directive to begin 'prompted' field 334 // beginField(short access, String name, String desc, Object value)335 void beginField(short access, String name, String desc, Object value) 336 throws jasError 337 { 338 ConstAttr ca = null; 339 340 if (value != null) { 341 CP cp; 342 // create a constant pool entry for the initial value 343 if (value instanceof Integer) { 344 cp = new IntegerCP(((Integer)value).intValue()); 345 } else if (value instanceof Float) { 346 cp = new FloatCP(((Float)value).floatValue()); 347 } else if (value instanceof Double) { 348 cp = new DoubleCP(((Double)value).doubleValue()); 349 } else if (value instanceof Long) { 350 cp = new LongCP(((Long)value).longValue()); 351 } else if (value instanceof String) { 352 cp = new StringCP((String)value); 353 } else { 354 throw new jasError("usupported value type"); 355 } 356 ca = new ConstAttr(cp); 357 } 358 cur_field = new Var(access, new AsciiCP(name), new AsciiCP(desc), ca); 359 } 360 361 // 362 // called by the .end field directive to end 'prompted' field 363 // endField()364 void endField() throws jasError { 365 if (cur_field == null) 366 throw new jasError(".end field without .field"); 367 368 if (cur_annotation != null) 369 opened_annotation("field"); 370 371 class_env.addField(cur_field); 372 cur_field = null; 373 } 374 // 375 // called by the .field directive 376 // addField(short access, String name, String desc, String sig, Object value)377 void addField(short access, String name, String desc, 378 String sig, Object value) throws jasError { 379 beginField(access, name, desc, value); 380 if (sig != null) cur_field.setSignature(new SignatureAttr(sig)); 381 endField(); 382 } 383 384 // 385 // called by the .method directive to start the definition for a method 386 // newMethod(String name, String descriptor, int access)387 void newMethod(String name, String descriptor, int access) { 388 // set method state variables 389 labels = new Hashtable(); 390 code = null; 391 except_attr = null; 392 catch_table = null; 393 var_table = null; 394 vtype_table = null; 395 line_table = null; 396 line_label_count = 0; 397 stack_map_label_count = 0; 398 stackmap = null; 399 verifyframe = null; 400 cur_method = new Method((short)access, new AsciiCP(name), 401 new AsciiCP(descriptor)); 402 } 403 404 405 // 406 // called by the .end method directive to end the definition for a method 407 // endMethod()408 void endMethod() throws jasError { 409 if (cur_method == null) 410 throw new jasError(".end method without .method"); 411 412 if (cur_annotation != null) 413 opened_annotation("method"); 414 415 if (code != null) { 416 417 plantLabel(END_METHOD); 418 flushInsnBuffer(); 419 420 if (catch_table != null) { 421 code.setCatchtable(catch_table); 422 } 423 424 if (var_table != null) { 425 code.setLocalVarTable(var_table); 426 } 427 if (vtype_table != null) { 428 code.setLocalVarTypeTable(vtype_table); 429 } 430 if (line_table != null) { 431 code.setLineTable(line_table); 432 } 433 if (stackmap != null) { 434 code.setStackMap(stackmap); 435 } 436 } 437 cur_method.setCode(code, except_attr); 438 class_env.addMethod(cur_method); 439 440 // clear method state variables 441 cur_method = null; 442 code = null; 443 labels = null; 444 catch_table = null; 445 line_table = null; 446 var_table = null; 447 vtype_table = null; 448 stackmap = null; 449 verifyframe = null; 450 } 451 452 // get last stackmap locals vector prevStack(int count)453 private Vector prevStack(int count) throws jasError { 454 Vector prev = null; 455 if(stackmap != null) 456 prev = stackmap.getLastFrame(count); 457 return prev; 458 } 459 460 // define a new stack map frame (possible with copy previous) beginStack(boolean copy)461 void beginStack(boolean copy) throws jasError { 462 Vector prev = null; 463 if(copy) 464 prev = prevStack(0); 465 verifyframe = new VerifyFrame(prev); 466 } 467 468 // define a new stack map frame and copy 'n' previous 469 // (type-independet) elements beginStack(int n)470 void beginStack(int n) throws jasError { 471 if(n <= 0) 472 throw new jasError("Invalid counter", true); 473 verifyframe = new VerifyFrame(prevStack(n)); 474 } 475 476 // define the offset of the current stack map frame plantStackOffset(int n)477 void plantStackOffset(int n) { 478 try { 479 verifyframe.setOffset(n); 480 } catch(jasError e) { 481 report_error(e.toString()); 482 } 483 } 484 plantStackOffset(String label)485 void plantStackOffset(String label) throws jasError { 486 Label l = getLabel(label); 487 try { 488 verifyframe.setOffset(l); 489 } catch(jasError e) { 490 report_error(e.toString()); 491 } 492 } 493 494 // add a local variable item to the current stack map frame plantStackLocals(String item, String val)495 void plantStackLocals(String item, String val) { 496 try { 497 verifyframe.addLocalsItem(item, val); 498 } catch(jasError e) { 499 report_error(e.toString()); 500 } 501 } 502 503 // add a stack item element to the current stack map frame plantStackStack(String item, String val)504 void plantStackStack(String item, String val) { 505 try { 506 verifyframe.addStackItem(item, val); 507 } catch(jasError e) { 508 report_error(e.toString()); 509 } 510 } 511 512 // add the current stack map frame to the current stack map attribute endStack()513 void endStack() { 514 if(!verifyframe.haveOffset()) { 515 String l = "jasmin_reserved_SM:" + (stack_map_label_count++); 516 try { 517 plantLabel(l); 518 verifyframe.setOffset(getLabel(l)); 519 } catch(jasError e) { 520 report_error(e.toString()); 521 } 522 } 523 if(stackmap==null) 524 stackmap = new StackMap(class_env); 525 526 stackmap.addFrame(verifyframe); 527 verifyframe = null; // PARANOYA 528 } 529 530 531 // 532 // plant routines - these use addInsn to add instructions to the 533 // code for the current method. 534 // 535 536 // 537 // used for instructions that take no arguments 538 // plant(String name)539 void plant(String name) throws jasError { 540 InsnInfo insn = InsnInfo.get(name); 541 autoNumber(); 542 flushInsnBuffer(); 543 544 if (insn.args.equals("")) { 545 bufferInsn(new Insn(insn.opcode)); 546 } else { 547 throw new jasError("Missing arguments for instruction " + name); 548 } 549 } 550 551 // 552 // used for relative branch targets (ie $+5, $-12, ...) 553 // plantRelativeGoto(String name, int val)554 void plantRelativeGoto(String name, int val) throws jasError { 555 InsnInfo insn = InsnInfo.get(name); 556 if (insn.args.equals("label")) { 557 bufferInsn(new Insn(insn.opcode, val, true)); 558 } else { // forward the request for "normal" instructions 559 plant(name, val); 560 } 561 } 562 563 564 // 565 // used for iinc 566 // plant(String name, int v1, int v2)567 void plant(String name, int v1, int v2) 568 throws jasError 569 { 570 InsnInfo insn = InsnInfo.get(name); 571 autoNumber(); 572 flushInsnBuffer(); 573 574 if (insn.args.equalsIgnoreCase("ii")) { 575 bufferInsn(new IincInsn(v1, v2, insn.args.charAt(0) == 'I')); 576 } else { 577 throw new jasError("Bad arguments for instruction " + name); 578 } 579 } 580 581 // 582 // used for instructions that take an integer parameter 583 // (branches are part of this, the int is converted to a label) 584 // plant(String name, int val)585 void plant(String name, int val) throws jasError { 586 InsnInfo insn = InsnInfo.get(name); 587 CodeAttr code = _getCode(); 588 autoNumber(); 589 flushInsnBuffer(); 590 591 if (insn.args.equalsIgnoreCase("i")) { 592 bufferInsn(new Insn(insn.opcode, val, insn.args.charAt(0) == 'I')); 593 } else if (insn.args.equals("constant")) { 594 bufferInsn(new Insn(insn.opcode, new IntegerCP(val))); 595 } else if (insn.args.equals("bigconstant")) { 596 bufferInsn(new Insn(insn.opcode, new LongCP(val))); 597 } else if (insn.args.equals("label")) { 598 plant(name, String.valueOf(val)); // the target is not signed 599 // assume it is a label 600 } else { 601 throw new jasError("Bad arguments for instruction " + name); 602 } 603 } 604 605 // 606 // used for ldc and other instructions that take a numeric argument 607 // plant(String name, Number val)608 void plant(String name, Number val) throws jasError { 609 InsnInfo insn = InsnInfo.get(name); 610 CodeAttr code = _getCode(); 611 autoNumber(); 612 flushInsnBuffer(); 613 614 if (insn.args.equalsIgnoreCase("i") && (val instanceof Integer)) { 615 bufferInsn(new Insn(insn.opcode, val.intValue(), insn.args.charAt(0) == 'I')); 616 } else if (insn.args.equals("constant")) { 617 if (val instanceof Integer || val instanceof Long) { 618 bufferInsn(new Insn(insn.opcode, 619 new IntegerCP(val.intValue()))); 620 } else if (val instanceof Float || val instanceof Double) { 621 bufferInsn(new Insn(insn.opcode, 622 new FloatCP(val.floatValue()))); 623 } 624 } else if (insn.args.equals("bigconstant")) { 625 if (val instanceof Integer || val instanceof Long) { 626 bufferInsn(new Insn(insn.opcode, 627 new LongCP(val.longValue()))); 628 } else if (val instanceof Float || val instanceof Double) { 629 bufferInsn(new Insn(insn.opcode, 630 new DoubleCP(val.doubleValue()))); 631 } 632 } else { 633 throw new jasError("Bad arguments for instruction " + name); 634 } 635 } 636 637 // 638 // used for ldc <quoted-string> 639 // plantString(String name, String val)640 void plantString(String name, String val) throws jasError { 641 InsnInfo insn = InsnInfo.get(name); 642 autoNumber(); 643 flushInsnBuffer(); 644 645 if (insn.args.equals("constant")) { 646 bufferInsn(new Insn(insn.opcode, new StringCP(val))); 647 } else { 648 throw new jasError("Bad arguments for instruction " + name); 649 } 650 } 651 652 // 653 // used for invokeinterface and multianewarray 654 // plant(String name, String val, int nargs)655 void plant(String name, String val, int nargs) 656 throws jasError 657 { 658 InsnInfo insn = InsnInfo.get(name); 659 CodeAttr code = _getCode(); 660 autoNumber(); 661 flushInsnBuffer(); 662 663 if (insn.args.equals("interface")) { 664 String split[] = ScannerUtils.splitClassMethodSignature(val); 665 bufferInsn(new InvokeinterfaceInsn( 666 new InterfaceCP(split[0], split[1], 667 split[2]), nargs)); 668 669 } else if (insn.args.equals("marray")) { 670 bufferInsn(new MultiarrayInsn(new ClassCP(val), nargs)); 671 } else { 672 throw new jasError("Bad arguments for instruction " + name); 673 } 674 } 675 676 // 677 // used for instructions that take a word as a parameter 678 // (e.g. branches, newarray, invokemethod) 679 // plant(String name, String val)680 void plant(String name, String val) throws jasError { 681 InsnInfo insn = InsnInfo.get(name); 682 CodeAttr code = _getCode(); 683 autoNumber(); 684 flushInsnBuffer(); 685 686 if (insn.args.equals("method")) { 687 String split[] = ScannerUtils.splitClassMethodSignature(val); 688 bufferInsn(new Insn(insn.opcode, 689 new MethodCP(split[0], split[1], split[2]))); 690 } else if (insn.args.equals("constant")) { 691 bufferInsn(new Insn(insn.opcode, new ClassCP(val))); 692 } else if (insn.args.equals("atype")) { 693 int atype = 0; 694 if (val.equals("boolean")) { 695 atype = 4; 696 } else if (val.equals("char")) { 697 atype = 5; 698 } else if (val.equals("float")) { 699 atype = 6; 700 } else if (val.equals("double")) { 701 atype = 7; 702 } else if (val.equals("byte")) { 703 atype = 8; 704 } else if (val.equals("short")) { 705 atype = 9; 706 } else if (val.equals("int")) { 707 atype = 10; 708 } else if (val.equals("long")) { 709 atype = 11; 710 } else { 711 throw new jasError("Bad array type: " + name); 712 } 713 bufferInsn(new Insn(insn.opcode, atype, false)); 714 } else if (insn.args.indexOf("label")>=0) { 715 bufferInsn(new Insn(insn.opcode, getLabel(val), scanner.line_num)); 716 } else if (insn.args.equals("class")) { 717 bufferInsn(new Insn(insn.opcode, new ClassCP(val))); 718 } else { 719 throw new jasError("(gloups)Bad arguments for instruction " + name); 720 } 721 } 722 723 // 724 // used for instructions that take a field and a signature as parameters 725 // (e.g. getstatic, putstatic) 726 // plant(String name, String v1, String v2)727 void plant(String name, String v1, String v2) 728 throws jasError 729 { 730 InsnInfo info = InsnInfo.get(name); 731 CodeAttr code = _getCode(); 732 autoNumber(); 733 flushInsnBuffer(); 734 735 if (info.args.equals("field")) { 736 String split[] = ScannerUtils.splitClassField(v1); 737 if(split[1] == null) 738 throw new jasError("can't extract field from "+v1); 739 bufferInsn(new Insn(info.opcode, 740 new FieldCP(split[0], split[1], v2))); 741 } else { 742 throw new jasError("Bad arguments for instruction " + name); 743 } 744 } 745 746 // 747 // Lookupswitch instruction 748 // newLookupswitch()749 void newLookupswitch() throws jasError { 750 switch_vec = new Vector(); 751 autoNumber(); 752 }; 753 addLookupswitch(int val, String label)754 void addLookupswitch(int val, String label) 755 throws jasError { 756 switch_vec.addElement(new Integer(val)); 757 switch_vec.addElement(new LabelOrOffset(getLabel(label))); 758 }; 759 addLookupswitch(int val, int offset)760 void addLookupswitch(int val, int offset) 761 throws jasError { 762 switch_vec.addElement(new Integer(val)); 763 switch_vec.addElement(new LabelOrOffset(offset)); 764 }; 765 endLookupswitch(String deflabel)766 void endLookupswitch(String deflabel) throws jasError { 767 flushInsnBuffer(); 768 Object[] offlabs = checkLookupswitch(); 769 int[] offsets = (int[])offlabs[0]; 770 LabelOrOffset[] labels = (LabelOrOffset[])offlabs[1]; 771 LabelOrOffset defl = new LabelOrOffset(getLabel(deflabel)); 772 bufferInsn(new LookupswitchInsn(defl, offsets, labels)); 773 } 774 endLookupswitch(int defoffset)775 void endLookupswitch(int defoffset) throws jasError { 776 flushInsnBuffer(); 777 Object[] offlabs = checkLookupswitch(); 778 int[] offsets = (int[])offlabs[0]; 779 LabelOrOffset[] labels = (LabelOrOffset[])offlabs[1]; 780 bufferInsn(new LookupswitchInsn(new LabelOrOffset(defoffset), 781 offsets, labels)); 782 } 783 784 785 // 786 // called by both endLookupswitch() methods 787 // checkLookupswitch()788 private Object[] checkLookupswitch() { 789 int n = switch_vec.size() >> 1; 790 int offsets[] = new int[n]; 791 LabelOrOffset labels[] = new LabelOrOffset[n]; 792 Enumeration e = switch_vec.elements(); 793 int i = 0; 794 while (e.hasMoreElements()) { 795 offsets[i] = ((Integer)e.nextElement()).intValue(); 796 labels[i] = (LabelOrOffset)e.nextElement(); 797 i++; 798 } 799 Object result[] = { offsets, labels }; 800 return result; 801 } 802 803 804 // 805 // Tableswitch instruction 806 // newTableswitch(int lowval)807 void newTableswitch(int lowval) throws jasError { 808 newTableswitch(lowval, -1); 809 }; 810 newTableswitch(int lowval, int hival)811 void newTableswitch(int lowval, int hival) throws jasError { 812 switch_vec = new Vector(); 813 low_value = lowval; 814 high_value = hival; 815 autoNumber(); 816 }; 817 addTableswitch(String label)818 void addTableswitch(String label) throws jasError { 819 switch_vec.addElement(new LabelOrOffset(getLabel(label))); 820 }; 821 addTableswitch(int offset)822 void addTableswitch(int offset) throws jasError { 823 switch_vec.addElement(new LabelOrOffset(offset)); 824 }; 825 endTableswitch(String deflabel)826 void endTableswitch(String deflabel) throws jasError { 827 flushInsnBuffer(); 828 LabelOrOffset labels[] = checkTableswitch(); 829 bufferInsn(new TableswitchInsn(low_value, 830 low_value+labels.length-1, 831 new LabelOrOffset(getLabel(deflabel)), 832 labels)); 833 } 834 endTableswitch(int defoffset)835 void endTableswitch(int defoffset) throws jasError { 836 flushInsnBuffer(); 837 LabelOrOffset labels[] = checkTableswitch(); 838 bufferInsn(new TableswitchInsn(low_value, 839 low_value+labels.length-1, new LabelOrOffset(defoffset), labels)); 840 } 841 842 843 // 844 // called by both endTableswitch() methods 845 // checkTableswitch()846 private LabelOrOffset[] checkTableswitch() { 847 int n = switch_vec.size(); 848 LabelOrOffset labels[] = new LabelOrOffset[n]; 849 Enumeration e = switch_vec.elements(); 850 int i = 0; 851 while (e.hasMoreElements()) { 852 labels[i] = (LabelOrOffset)e.nextElement(); 853 i++; 854 } 855 if (high_value != -1 && (high_value != low_value + n - 1)) { 856 report_error("tableswitch - given incorrect value for <high>"); 857 } 858 return labels; 859 } 860 861 862 // Used by the parser to tell ClassFile what the line number 863 // for the next statement is. ClassFile's autoNumber mechanism 864 // uses this info. setLine(int l)865 void setLine(int l) { line_num = l; } 866 867 // 868 // If auto_number is true, output debugging line number table 869 // for Jasmin assembly instructions. 870 871 // autoNumber()872 void autoNumber() throws jasError { 873 if (auto_number) { 874 // use the line number of the last token 875 addLineInfo(line_num); 876 } 877 } 878 879 // 880 // Label management 881 // 882 883 // 884 // gets the Label object for a label, creating it if it doesn't exist 885 // getLabel(String name)886 Label getLabel(String name) throws jasError { 887 888 // check that we are inside of a method definition 889 if (cur_method == null) { 890 throw new jasError( "illegal use of label outside of method definition"); 891 } 892 893 Label lab = (Label)labels.get(name); 894 if (lab == null) { 895 lab = new Label(name); 896 labels.put(name, lab); 897 } 898 return lab; 899 } 900 901 // 902 // defines a label 903 // plantLabel(String name)904 void plantLabel(String name) throws jasError { 905 // System.out.println("planting label "+name); 906 try { 907 Integer.parseInt(name); 908 // the label is a number, we must add it *before* the buffered insn 909 // this is the reason to use a buffer instructions 910 _getCode().addInsn(getLabel(name)); 911 flushInsnBuffer(); 912 } catch(NumberFormatException e) { 913 // traditional label (word), add it *after* the buffered insn 914 flushInsnBuffer(); 915 bufferInsn(getLabel(name)); 916 } 917 } 918 919 // if there is an instruction in the buffer, add it to the code 920 // and put the new instruction in the buffer bufferInsn(Insn i)921 void bufferInsn(Insn i) throws jasError{ 922 if(i == null) 923 throw new jasError("trying to buffer a null instruction"); 924 if(buffered_insn != null) 925 flushInsnBuffer(); 926 buffered_insn = i; 927 } 928 929 // if the buffer is not empty, add the instruction to the code array flushInsnBuffer()930 void flushInsnBuffer() throws jasError { 931 if(buffered_insn != null) { 932 _getCode().addInsn(buffered_insn); 933 buffered_insn = null; 934 } 935 } 936 937 // 938 // used by the .var directive 939 // addVar(String startLab, String endLab, String name, String desc, String sign, int var_num)940 void addVar(String startLab, String endLab, 941 String name, String desc, String sign, int var_num) 942 throws jasError { 943 if (startLab == null) { 944 startLab = BGN_METHOD; 945 } 946 947 if (endLab == null) { 948 endLab = END_METHOD; 949 } 950 Label slab, elab; 951 slab = getLabel(startLab); 952 elab = getLabel(endLab); 953 954 if (var_table == null) { 955 var_table = new LocalVarTableAttr(); 956 } 957 958 var_table.addEntry(new LocalVarEntry(slab, elab, name, desc, var_num)); 959 960 if (sign != null) { 961 if (vtype_table == null) { 962 vtype_table = new LocalVarTypeTableAttr(); 963 } 964 965 vtype_table.addEntry(new LocalVarEntry(slab, elab, name, sign, var_num)); 966 } 967 } 968 addVar(int startOffset, int endOffset, String name, String desc, String sign, int var_num)969 void addVar(int startOffset, int endOffset, String name, 970 String desc, String sign, int var_num) throws jasError { 971 if (var_table == null) { 972 var_table = new LocalVarTableAttr(); 973 } 974 var_table.addEntry(new LocalVarEntry(startOffset, endOffset, 975 name, desc, var_num)); 976 977 if (sign != null) { 978 if (vtype_table == null) { 979 vtype_table = new LocalVarTypeTableAttr(); 980 } 981 982 vtype_table.addEntry(new LocalVarEntry(startOffset, endOffset, 983 name, sign, var_num)); 984 } 985 } 986 987 // 988 // used by .line directive 989 // addLineInfo(int line_num)990 void addLineInfo(int line_num) throws jasError { 991 String l = "jasmin_reserved_L:" + (line_label_count++); 992 if (line_table == null) { 993 line_table = new LineTableAttr(); 994 } 995 plantLabel(l); 996 line_table.addEntry(getLabel(l), line_num); 997 } 998 addLine(int line_num)999 void addLine(int line_num) throws jasError { 1000 if (!auto_number) { 1001 addLineInfo(line_num); 1002 } 1003 } 1004 1005 // 1006 // used by the .throws directive 1007 // addThrow(String name)1008 void addThrow(String name) throws jasError { 1009 1010 // check that we are inside of a method definition 1011 if (cur_method == null) { 1012 throw new jasError( "illegal use of .throw outside of method definition"); 1013 } 1014 1015 if (except_attr == null) { 1016 except_attr = new ExceptAttr(); 1017 } 1018 except_attr.addException(new ClassCP(name)); 1019 } 1020 1021 // 1022 // used by the .catch directive 1023 // addCatch(String name, String start_lab, String end_lab, String branch_lab)1024 void addCatch(String name, String start_lab, String end_lab, 1025 String branch_lab) throws jasError { 1026 ClassCP class_cp = checkCatch(name); 1027 1028 catch_table.addEntry(getLabel(start_lab), getLabel(end_lab), 1029 getLabel(branch_lab), class_cp); 1030 } 1031 1032 addCatch(String name, int start_off, int end_off, int branch_off)1033 void addCatch(String name, int start_off, int end_off, 1034 int branch_off) throws jasError { 1035 ClassCP class_cp = checkCatch(name); 1036 1037 catch_table.addEntry(start_off, end_off, branch_off, class_cp); 1038 } 1039 checkLimit(int v)1040 short checkLimit(int v) throws jasError { 1041 if(v < 0 || v > 65535) 1042 throw new jasError("Illegal limit value", true); 1043 return (short)v; 1044 } 1045 1046 // 1047 // used by the .limit stack directive 1048 // setStackSize(int v)1049 void setStackSize(int v) throws jasError { 1050 _getCode().setStackSize(checkLimit(v)); 1051 } 1052 1053 // 1054 // used by the .limit vars directive 1055 // setVarSize(int v)1056 void setVarSize(int v) throws jasError { 1057 _getCode().setVarSize(checkLimit(v)); 1058 } 1059 1060 // --- Private stuff --- 1061 1062 1063 // 1064 // verifications made by both addCatch() methods 1065 // checkCatch(String name)1066 private ClassCP checkCatch(String name) throws jasError { 1067 ClassCP class_cp; 1068 // check that we are inside of a method definition 1069 if (cur_method == null) { 1070 throw new jasError( "illegal use of .catch outside of method definition"); 1071 } 1072 1073 if (catch_table == null) { 1074 catch_table = new Catchtable(); 1075 } 1076 1077 if (name.equals("all")) { 1078 class_cp = null; 1079 } else { 1080 class_cp = new ClassCP(name); 1081 } 1082 return class_cp; 1083 } 1084 1085 1086 // 1087 // returns the code block, creating it if it doesn't exist 1088 // _getCode()1089 CodeAttr _getCode() throws jasError { 1090 1091 // check that we are inside of a method definition 1092 if (cur_method == null) { 1093 throw new jasError("illegal use of instruction outside of method definition"); 1094 } 1095 1096 if (code == null) { 1097 code = new CodeAttr(); 1098 plantLabel(BGN_METHOD); 1099 } 1100 1101 return (code); 1102 } 1103 1104 1105 // PUBLIC API TO JASMIN: 1106 1107 /** Makes a new ClassFile object, used to represent a Java class file. 1108 * You can then use readJasmin to read in a class file stored in 1109 * Jasmin assembly format. 1110 */ 1111 ClassFile()1112 public ClassFile() {} 1113 1114 /** 1115 * Parses a Jasmin file, converting it internally into a binary 1116 * representation. 1117 * If something goes wrong, this throws one of 1118 * an IOException, or a jasError, or one of a few other exceptions. 1119 * I'll tie this down more formally in the next version. 1120 * 1121 * @param input is the stream containing the Jasmin assembly code for the 1122 * class. 1123 * 1124 * @param name is the name of the stream. This name will be 1125 * concatenated to error messages printed to System.err. 1126 * 1127 * @param numberLines true if you want Jasmin to generate line 1128 * numbers automatically, based on the assembly source, or 1129 * false if you are using the ".line" directive and don't 1130 * want Jasmin to help out. 1131 */ readJasmin(Reader input, String name, boolean numberLines)1132 public void readJasmin(Reader input, String name, 1133 boolean numberLines) 1134 throws IOException, Exception { 1135 // initialize variables for error reporting 1136 errors = 0; 1137 filename = name; 1138 source_name = name; 1139 1140 //initialize local-frame variables 1141 cur_method = null; 1142 cur_field = null; 1143 cur_annotation = null; 1144 class_header = false; 1145 1146 // if numberLines is true, we output LineTableAttr's that indicate what line 1147 // numbers the Jasmin code falls on. 1148 auto_number = numberLines; 1149 1150 // Parse the input file 1151 class_env = new ClassEnv(); 1152 1153 scanner = new Scanner(input); 1154 parser parse_obj = new parser(this, scanner); 1155 1156 if (PARSER_DEBUG) { 1157 // for debugging 1158 parse_obj.debug_parse(); 1159 } else { 1160 parse_obj.parse(); 1161 } 1162 } 1163 1164 /** 1165 * Returns the number of warnings/errors encountered while parsing a file. 1166 * 0 if everything went OK. 1167 */ errorCount()1168 public int errorCount() { 1169 return errors; 1170 } 1171 1172 /** 1173 * Returns the name of the class in the file (i.e. the string given to 1174 * the .class parameter in Jasmin) 1175 * 1176 */ getClassName()1177 public String getClassName() { 1178 return class_name; 1179 } 1180 1181 /** 1182 * Writes the binary data for the class represented by this ClassFile 1183 * object to the specified 1184 * output stream, using the Java Class File format. Throws either an 1185 * IOException or a jasError if something goes wrong. 1186 */ write(OutputStream outp)1187 public void write(OutputStream outp) throws IOException, jasError { 1188 class_env.setSource(source_name); 1189 class_env.write(new DataOutputStream(outp)); 1190 } 1191 }; 1192 1193 /* --- Revision History --------------------------------------------------- 1194 --- Iouri Kharon, Aug 10 2006 1195 Added 'Wide' prefix support to some instructions 1196 1197 --- Iouri Kharon, Feb 17 2006 1198 Added extended syntax for .stack 1199 1200 --- Iouri Kharon, Dec 30 2005 1201 Added multiline .field, directive .inner and .anotation 1202 1203 --- Iouri Kharon, Dec 20 2005 1204 Added LocalVariableTypeTable support in addVar methods (called by .var) 1205 1206 --- Daniel Reynaud, Oct 22 2005 1207 Added setSourceDebugExtension() method (called by .debug) 1208 Added setEnclosingMethod() method (called by .enclosing) 1209 1210 --- Daniel Reynaud, Oct 19 2005 1211 Added setVersion() method (called by .bytecode) 1212 Changed BGN_METHOD, END_METHOD and line number label to avoid collision 1213 1214 --- Jonathan Meyer, April 11 1997 1215 Fixed bug where source_name was not being set in class_env. 1216 1217 --- Jonathan Meyer, Mar 1 1997 1218 Renamed "Jasmin" class "ClassFile". 1219 1220 --- Jonathan Meyer, Feb 8 1997 1221 Converted to non-static. Made a public API. Split off InsnInfo to a 1222 separate file. 1223 1224 --- Jonathan Meyer, Oct 1 1996 1225 Added addInterface method, used by the .implements directive. 1226 1227 --- Jonathan Meyer, July 25 1996 1228 Added setLine and line_num, to fix problem with autoNumber. 1229 Added report_error method. 1230 1231 --- Jonathan Meyer, July 24 1996 added version constant. 1232 */ 1233