1 /* 2 * Copyright (c) 2010, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- 26 27 import java.util.*; 28 import java.lang.reflect.*; 29 import java.io.*; 30 import xmlkit.XMLKit.Element; 31 /* 32 * @author jrose 33 */ 34 public class ClassWriter extends ClassSyntax implements ClassSyntax.GetCPIndex { 35 36 private static final CommandLineParser CLP = new CommandLineParser("" 37 + "-source: +> = \n" 38 + "-dest: +> = \n" 39 + "-encoding: +> = \n" 40 + "-parseBytes $ \n" 41 + "- *? \n" 42 + "\n"); 43 44 public static void main(String[] ava) throws IOException { 45 ArrayList<String> av = new ArrayList<String>(Arrays.asList(ava)); 46 HashMap<String, String> props = new HashMap<String, String>(); 47 props.put("-encoding:", "UTF8"); // default 48 CLP.parse(av, props); 49 File source = asFile(props.get("-source:")); 50 File dest = asFile(props.get("-dest:")); 51 String encoding = props.get("-encoding:"); 52 boolean parseBytes = props.containsKey("-parseBytes"); 53 boolean destMade = false; 54 55 for (String a : av) { 56 File f; 57 File inf = new File(source, a); 58 System.out.println("Reading " + inf); 59 Element e; 60 if (inf.getName().endsWith(".class")) { 61 ClassReader cr = new ClassReader(); 62 cr.parseBytes = parseBytes; 63 e = cr.readFrom(inf); 64 f = new File(a); 65 } else if (inf.getName().endsWith(".xml")) { 66 InputStream in = new FileInputStream(inf); 67 Reader inw = ClassReader.makeReader(in, encoding); 68 e = XMLKit.readFrom(inw); 69 e.findAllInTree(XMLKit.and(XMLKit.elementFilter(nonAttrTags()), 70 XMLKit.methodFilter(Element.method("trimText")))); 71 //System.out.println(e); 72 inw.close(); 73 f = new File(a.substring(0, a.length() - ".xml".length()) + ".class"); 74 } else { 75 System.out.println("Warning: unknown input " + a); 76 continue; 77 } 78 // Now write it: 79 if (!destMade) { 80 destMade = true; 81 if (dest == null) { 82 dest = File.createTempFile("TestOut", ".dir", new File(".")); 83 dest.delete(); 84 System.out.println("Writing results to " + dest); 85 } 86 if (!(dest.isDirectory() || dest.mkdir())) { 87 throw new RuntimeException("Cannot create " + dest); 88 } 89 } 90 File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath()); 91 outf.getParentFile().mkdirs(); 92 new ClassWriter(e).writeTo(outf); 93 } 94 } 95 96 private static File asFile(String str) { 97 return (str == null) ? null : new File(str); 98 } 99 100 public void writeTo(File file) throws IOException { 101 OutputStream out = null; 102 try { 103 out = new BufferedOutputStream(new FileOutputStream(file)); 104 writeTo(out); 105 } finally { 106 if (out != null) { 107 out.close(); 108 } 109 } 110 } 111 protected String[] callables; // varies 112 protected int cpoolSize = 0; 113 protected HashMap<String, String> attrTypesByTag; 114 protected OutputStream out; 115 protected HashMap<String, int[]> cpMap = new HashMap<String, int[]>(); 116 protected ArrayList<ByteArrayOutputStream> attrBufs = new ArrayList<ByteArrayOutputStream>(); 117 118 private void setupAttrTypes() { 119 attrTypesByTag = new HashMap<String, String>(); 120 for (String key : attrTypes.keySet()) { 121 String pfx = key.substring(0, key.indexOf('.') + 1); 122 String val = attrTypes.get(key); 123 int pos = val.indexOf('<'); 124 if (pos >= 0) { 125 String tag = val.substring(pos + 1, val.indexOf('>', pos)); 126 attrTypesByTag.put(pfx + tag, key); 127 } 128 } 129 //System.out.println("attrTypesByTag: "+attrTypesByTag); 130 } 131 132 protected ByteArrayOutputStream getAttrBuf() { 133 int nab = attrBufs.size(); 134 if (nab == 0) { 135 return new ByteArrayOutputStream(1024); 136 } 137 ByteArrayOutputStream ab = attrBufs.get(nab - 1); 138 attrBufs.remove(nab - 1); 139 return ab; 140 } 141 142 protected void putAttrBuf(ByteArrayOutputStream ab) { 143 ab.reset(); 144 attrBufs.add(ab); 145 } 146 147 public ClassWriter(Element root) { 148 this(root, null); 149 } 150 151 public ClassWriter(Element root, ClassSyntax cr) { 152 if (cr != null) { 153 attrTypes = cr.attrTypes; 154 } 155 setupAttrTypes(); 156 if (root.getName() == "ClassFile") { 157 cfile = root; 158 cpool = root.findElement("ConstantPool"); 159 klass = root.findElement("Class"); 160 } else if (root.getName() == "Class") { 161 cfile = new Element("ClassFile", 162 new String[]{ 163 "magic", String.valueOf(0xCAFEBABE), 164 "minver", "0", "majver", "46",}); 165 cpool = new Element("ConstantPool"); 166 klass = root; 167 } else { 168 throw new IllegalArgumentException("bad element type " + root.getName()); 169 } 170 if (cpool == null) { 171 cpool = new Element("ConstantPool"); 172 } 173 174 int cpLen = 1 + cpool.size(); 175 for (Element c : cpool.elements()) { 176 int id = (int) c.getAttrLong("id"); 177 int tag = cpTagValue(c.getName()); 178 setCPIndex(tag, c.getText().toString(), id); 179 switch (tag) { 180 case CONSTANT_Long: 181 case CONSTANT_Double: 182 cpLen += 1; 183 } 184 } 185 cpoolSize = cpLen; 186 } 187 188 public int findCPIndex(int tag, String name) { 189 if (name == null) { 190 return 0; 191 } 192 int[] ids = cpMap.get(name.toString()); 193 return (ids == null) ? 0 : ids[tag]; 194 } 195 196 public int getCPIndex(int tag, String name) { 197 //System.out.println("getCPIndex "+cpTagName(tag)+" "+name); 198 if (name == null) { 199 return 0; 200 } 201 int id = findCPIndex(tag, name); 202 if (id == 0) { 203 id = cpoolSize; 204 cpoolSize += 1; 205 setCPIndex(tag, name, id); 206 cpool.add(new Element(cpTagName(tag), 207 new String[]{"id", "" + id}, 208 new Object[]{name})); 209 int pos; 210 switch (tag) { 211 case CONSTANT_Long: 212 case CONSTANT_Double: 213 cpoolSize += 1; 214 break; 215 case CONSTANT_Class: 216 case CONSTANT_String: 217 getCPIndex(CONSTANT_Utf8, name); 218 break; 219 case CONSTANT_Fieldref: 220 case CONSTANT_Methodref: 221 case CONSTANT_InterfaceMethodref: 222 pos = name.indexOf(' '); 223 getCPIndex(CONSTANT_Class, name.substring(0, pos)); 224 getCPIndex(CONSTANT_NameAndType, name.substring(pos + 1)); 225 break; 226 case CONSTANT_NameAndType: 227 pos = name.indexOf(' '); 228 getCPIndex(CONSTANT_Utf8, name.substring(0, pos)); 229 getCPIndex(CONSTANT_Utf8, name.substring(pos + 1)); 230 break; 231 } 232 } 233 return id; 234 } 235 236 public void setCPIndex(int tag, String name, int id) { 237 //System.out.println("setCPIndex id="+id+" tag="+tag+" name="+name); 238 int[] ids = cpMap.get(name); 239 if (ids == null) { 240 cpMap.put(name, ids = new int[13]); 241 } 242 if (ids[tag] != 0 && ids[tag] != id) { 243 System.out.println("Warning: Duplicate CP entries for " + ids[tag] + " and " + id); 244 } 245 //assert(ids[tag] == 0 || ids[tag] == id); 246 ids[tag] = id; 247 } 248 249 public int parseFlags(String flagString) { 250 int flags = 0; 251 int i = -1; 252 for (String[] names : modifierNames) { 253 ++i; 254 for (String name : names) { 255 if (name == null) { 256 continue; 257 } 258 int pos = flagString.indexOf(name); 259 if (pos >= 0) { 260 flags |= (1 << i); 261 } 262 } 263 } 264 return flags; 265 } 266 267 public void writeTo(OutputStream realOut) throws IOException { 268 OutputStream headOut = realOut; 269 ByteArrayOutputStream tailOut = new ByteArrayOutputStream(); 270 271 // write the body of the class file first 272 this.out = tailOut; 273 writeClass(); 274 275 // write the file header last 276 this.out = headOut; 277 u4((int) cfile.getAttrLong("magic")); 278 u2((int) cfile.getAttrLong("minver")); 279 u2((int) cfile.getAttrLong("majver")); 280 writeCP(); 281 282 // recopy the file tail 283 this.out = null; 284 tailOut.writeTo(realOut); 285 } 286 287 void writeClass() throws IOException { 288 int flags = parseFlags(klass.getAttr("flags")); 289 flags ^= Modifier.SYNCHRONIZED; 290 u2(flags); 291 cpRef(CONSTANT_Class, klass.getAttr("name")); 292 cpRef(CONSTANT_Class, klass.getAttr("super")); 293 Element interfaces = klass.findAllElements("Interface"); 294 u2(interfaces.size()); 295 for (Element e : interfaces.elements()) { 296 cpRef(CONSTANT_Class, e.getAttr("name")); 297 } 298 for (int isMethod = 0; isMethod <= 1; isMethod++) { 299 Element members = klass.findAllElements(isMethod != 0 ? "Method" : "Field"); 300 u2(members.size()); 301 for (Element m : members.elements()) { 302 writeMember(m, isMethod != 0); 303 } 304 } 305 writeAttributesFor(klass); 306 } 307 308 private void writeMember(Element member, boolean isMethod) throws IOException { 309 //System.out.println("writeMember "+member); 310 u2(parseFlags(member.getAttr("flags"))); 311 cpRef(CONSTANT_Utf8, member.getAttr("name")); 312 cpRef(CONSTANT_Utf8, member.getAttr("type")); 313 writeAttributesFor(member); 314 } 315 316 protected void writeAttributesFor(Element x) throws IOException { 317 LinkedHashSet<String> attrNames = new LinkedHashSet<String>(); 318 for (Element e : x.elements()) { 319 attrNames.add(e.getName()); // uniquifying 320 } 321 attrNames.removeAll(nonAttrTags()); 322 u2(attrNames.size()); 323 if (attrNames.isEmpty()) { 324 return; 325 } 326 Element prevCurrent; 327 if (x.getName() == "Code") { 328 prevCurrent = currentCode; 329 currentCode = x; 330 } else { 331 prevCurrent = currentMember; 332 currentMember = x; 333 } 334 OutputStream realOut = this.out; 335 for (String utag : attrNames) { 336 String qtag = x.getName() + "." + utag; 337 String wtag = "*." + utag; 338 String key = attrTypesByTag.get(qtag); 339 if (key == null) { 340 key = attrTypesByTag.get(wtag); 341 } 342 String type = attrTypes.get(key); 343 //System.out.println("tag "+qtag+" => key "+key+"; type "+type); 344 Element attrs = x.findAllElements(utag); 345 ByteArrayOutputStream attrBuf = getAttrBuf(); 346 if (type == null) { 347 if (attrs.size() != 1 || !attrs.get(0).equals(new Element(utag))) { 348 System.out.println("Warning: No attribute type description: " + qtag); 349 } 350 key = wtag; 351 } else { 352 try { 353 this.out = attrBuf; 354 // unparse according to type desc. 355 if (type.equals("<Code>...")) { 356 writeCode((Element) attrs.get(0)); // assume only 1 357 } else if (type.equals("<Frame>...")) { 358 writeStackMap(attrs, false); 359 } else if (type.equals("<FrameX>...")) { 360 writeStackMap(attrs, true); 361 } else if (type.startsWith("[")) { 362 writeAttributeRecursive(attrs, type); 363 } else { 364 writeAttribute(attrs, type); 365 } 366 } finally { 367 //System.out.println("Attr Bytes = \""+attrBuf.toString(EIGHT_BIT_CHAR_ENCODING).replace('"', (char)('"'|0x80))+"\""); 368 this.out = realOut; 369 } 370 } 371 cpRef(CONSTANT_Utf8, key.substring(key.indexOf('.') + 1)); 372 u4(attrBuf.size()); 373 attrBuf.writeTo(out); 374 putAttrBuf(attrBuf); 375 } 376 if (x.getName() == "Code") { 377 currentCode = prevCurrent; 378 } else { 379 currentMember = prevCurrent; 380 } 381 } 382 383 private void writeAttributeRecursive(Element aval, String type) throws IOException { 384 assert (callables == null); 385 callables = getBodies(type); 386 writeAttribute(aval, callables[0]); 387 callables = null; 388 } 389 390 private void writeAttribute(Element aval, String type) throws IOException { 391 //System.out.println("writeAttribute "+aval+" using "+type); 392 String nextAttrName = null; 393 boolean afterElemHead = false; 394 for (int len = type.length(), next, i = 0; i < len; i = next) { 395 int value; 396 char intKind; 397 int tag; 398 int sigChar; 399 String attrValue; 400 switch (type.charAt(i)) { 401 case '<': 402 assert (nextAttrName == null); 403 next = type.indexOf('>', i); 404 String form = type.substring(i + 1, next++); 405 if (form.indexOf('=') < 0) { 406 // elem_placement = '<' elemname '>' 407 if (aval.isAnonymous()) { 408 assert (aval.size() == 1); 409 aval = (Element) aval.get(0); 410 } 411 assert (aval.getName().equals(form)) : aval + " // " + form; 412 afterElemHead = true; 413 } else { 414 // attr_placement = '(' attrname '=' (value)? ')' 415 int eqPos = form.indexOf('='); 416 assert (eqPos >= 0); 417 nextAttrName = form.substring(0, eqPos).intern(); 418 if (eqPos != form.length() - 1) { 419 // value is implicit, not placed in file 420 nextAttrName = null; 421 } 422 afterElemHead = false; 423 } 424 continue; 425 case '(': 426 next = type.indexOf(')', ++i); 427 int callee = Integer.parseInt(type.substring(i, next++)); 428 writeAttribute(aval, callables[callee]); 429 continue; 430 case 'N': // replication = 'N' int '[' type ... ']' 431 { 432 assert (nextAttrName == null); 433 afterElemHead = false; 434 char countType = type.charAt(i + 1); 435 next = i + 2; 436 String type1 = getBody(type, next); 437 Element elems = aval; 438 if (type1.startsWith("<")) { 439 // Select only matching members of aval. 440 String elemName = type1.substring(1, type1.indexOf('>')); 441 elems = aval.findAllElements(elemName); 442 } 443 putInt(elems.size(), countType); 444 next += type1.length() + 2; // skip body and brackets 445 for (Element elem : elems.elements()) { 446 writeAttribute(elem, type1); 447 } 448 } 449 continue; 450 case 'T': // union = 'T' any_int union_case* '(' ')' '[' body ']' 451 // write the value 452 value = (int) aval.getAttrLong("tag"); 453 assert (aval.getAttr("tag") != null) : aval; 454 intKind = type.charAt(++i); 455 if (intKind == 'S') { 456 intKind = type.charAt(++i); 457 } 458 putInt(value, intKind); 459 nextAttrName = null; 460 afterElemHead = false; 461 ++i; // skip the int type char 462 // union_case = '(' ('-')? digit+ ')' '[' body ']' 463 for (boolean foundCase = false;;) { 464 assert (type.charAt(i) == '('); 465 next = type.indexOf(')', ++i); 466 assert (next >= i); 467 String caseStr = type.substring(i, next++); 468 String type1 = getBody(type, next); 469 next += type1.length() + 2; // skip body and brackets 470 boolean lastCase = (caseStr.length() == 0); 471 if (!foundCase 472 && (lastCase || matchTag(value, caseStr))) { 473 foundCase = true; 474 // Execute this body. 475 writeAttribute(aval, type1); 476 } 477 if (lastCase) { 478 break; 479 } 480 } 481 continue; 482 case 'B': 483 case 'H': 484 case 'I': // int = oneof "BHI" 485 value = (int) aval.getAttrLong(nextAttrName); 486 intKind = type.charAt(i); 487 next = i + 1; 488 break; 489 case 'K': 490 sigChar = type.charAt(i + 1); 491 if (sigChar == 'Q') { 492 assert (currentMember.getName() == "Field"); 493 assert (aval.getName() == "ConstantValue"); 494 String sig = currentMember.getAttr("type"); 495 sigChar = sig.charAt(0); 496 switch (sigChar) { 497 case 'Z': 498 case 'B': 499 case 'C': 500 case 'S': 501 sigChar = 'I'; 502 break; 503 } 504 } 505 switch (sigChar) { 506 case 'I': 507 tag = CONSTANT_Integer; 508 break; 509 case 'J': 510 tag = CONSTANT_Long; 511 break; 512 case 'F': 513 tag = CONSTANT_Float; 514 break; 515 case 'D': 516 tag = CONSTANT_Double; 517 break; 518 case 'L': 519 tag = CONSTANT_String; 520 break; 521 default: 522 assert (false); 523 tag = 0; 524 } 525 assert (type.charAt(i + 2) == 'H'); // only H works for now 526 next = i + 3; 527 assert (afterElemHead || nextAttrName != null); 528 //System.out.println("get attr "+nextAttrName+" in "+aval); 529 if (nextAttrName != null) { 530 attrValue = aval.getAttr(nextAttrName); 531 assert (attrValue != null); 532 } else { 533 assert (aval.isText()) : aval; 534 attrValue = aval.getText().toString(); 535 } 536 value = getCPIndex(tag, attrValue); 537 intKind = 'H'; //type.charAt(i+2); 538 break; 539 case 'R': 540 sigChar = type.charAt(i + 1); 541 switch (sigChar) { 542 case 'C': 543 tag = CONSTANT_Class; 544 break; 545 case 'S': 546 tag = CONSTANT_Utf8; 547 break; 548 case 'D': 549 tag = CONSTANT_Class; 550 break; 551 case 'F': 552 tag = CONSTANT_Fieldref; 553 break; 554 case 'M': 555 tag = CONSTANT_Methodref; 556 break; 557 case 'I': 558 tag = CONSTANT_InterfaceMethodref; 559 break; 560 case 'U': 561 tag = CONSTANT_Utf8; 562 break; 563 //case 'Q': tag = CONSTANT_Class; break; 564 default: 565 assert (false); 566 tag = 0; 567 } 568 assert (type.charAt(i + 2) == 'H'); // only H works for now 569 next = i + 3; 570 assert (afterElemHead || nextAttrName != null); 571 //System.out.println("get attr "+nextAttrName+" in "+aval); 572 if (nextAttrName != null) { 573 attrValue = aval.getAttr(nextAttrName); 574 } else if (aval.hasText()) { 575 attrValue = aval.getText().toString(); 576 } else { 577 attrValue = null; 578 } 579 value = getCPIndex(tag, attrValue); 580 intKind = 'H'; //type.charAt(i+2); 581 break; 582 case 'P': // bci = 'P' int 583 case 'S': // signed_int = 'S' int 584 next = i + 2; 585 value = (int) aval.getAttrLong(nextAttrName); 586 intKind = type.charAt(i + 1); 587 break; 588 case 'F': 589 next = i + 2; 590 value = parseFlags(aval.getAttr(nextAttrName)); 591 intKind = type.charAt(i + 1); 592 break; 593 default: 594 throw new RuntimeException("bad attr format '" + type.charAt(i) + "': " + type); 595 } 596 // write the value 597 putInt(value, intKind); 598 nextAttrName = null; 599 afterElemHead = false; 600 } 601 assert (nextAttrName == null); 602 } 603 604 private void putInt(int x, char ch) throws IOException { 605 switch (ch) { 606 case 'B': 607 u1(x); 608 break; 609 case 'H': 610 u2(x); 611 break; 612 case 'I': 613 u4(x); 614 break; 615 } 616 assert ("BHI".indexOf(ch) >= 0); 617 } 618 619 private void writeCode(Element code) throws IOException { 620 //System.out.println("writeCode "+code); 621 //Element m = new Element(currentMember); m.remove(code); 622 //System.out.println(" in "+m); 623 int stack = (int) code.getAttrLong("stack"); 624 int local = (int) code.getAttrLong("local"); 625 Element bytes = code.findElement("Bytes"); 626 Element insns = code.findElement("Instructions"); 627 String bytecodes; 628 if (insns == null) { 629 bytecodes = bytes.getText().toString(); 630 } else { 631 bytecodes = InstructionSyntax.assemble(insns, this); 632 // Cache the assembled bytecodes: 633 bytes = new Element("Bytes", (String[]) null, bytecodes); 634 code.add(0, bytes); 635 } 636 u2(stack); 637 u2(local); 638 int length = bytecodes.length(); 639 u4(length); 640 for (int i = 0; i < length; i++) { 641 u1((byte) bytecodes.charAt(i)); 642 } 643 Element handlers = code.findAllElements("Handler"); 644 u2(handlers.size()); 645 for (Element handler : handlers.elements()) { 646 int start = (int) handler.getAttrLong("start"); 647 int end = (int) handler.getAttrLong("end"); 648 int catsh = (int) handler.getAttrLong("catch"); 649 u2(start); 650 u2(end); 651 u2(catsh); 652 cpRef(CONSTANT_Class, handler.getAttr("class")); 653 } 654 writeAttributesFor(code); 655 } 656 657 protected void writeStackMap(Element attrs, boolean hasXOption) throws IOException { 658 Element bytes = currentCode.findElement("Bytes"); 659 assert (bytes != null && bytes.size() == 1); 660 int byteLength = ((String) bytes.get(0)).length(); 661 boolean uoffsetIsU4 = (byteLength >= (1 << 16)); 662 boolean ulocalvarIsU4 = currentCode.getAttrLong("local") >= (1 << 16); 663 boolean ustackIsU4 = currentCode.getAttrLong("stack") >= (1 << 16); 664 if (uoffsetIsU4) { 665 u4(attrs.size()); 666 } else { 667 u2(attrs.size()); 668 } 669 for (Element frame : attrs.elements()) { 670 int bci = (int) frame.getAttrLong("bci"); 671 if (uoffsetIsU4) { 672 u4(bci); 673 } else { 674 u2(bci); 675 } 676 if (hasXOption) { 677 u1((int) frame.getAttrLong("flags")); 678 } 679 // Scan local and stack types in this frame: 680 final int LOCALS = 0, STACK = 1; 681 for (int j = LOCALS; j <= STACK; j++) { 682 Element types = frame.findElement(j == LOCALS ? "Local" : "Stack"); 683 int typeSize = (types == null) ? 0 : types.size(); 684 if (j == LOCALS) { 685 if (ulocalvarIsU4) { 686 u4(typeSize); 687 } else { 688 u2(typeSize); 689 } 690 } else { // STACK 691 if (ustackIsU4) { 692 u4(typeSize); 693 } else { 694 u2(typeSize); 695 } 696 } 697 if (types == null) { 698 continue; 699 } 700 for (Element type : types.elements()) { 701 int tag = itemTagValue(type.getName()); 702 u1(tag); 703 switch (tag) { 704 case ITEM_Object: 705 cpRef(CONSTANT_Class, type.getAttr("class")); 706 break; 707 case ITEM_Uninitialized: 708 case ITEM_ReturnAddress: { 709 int offset = (int) type.getAttrLong("bci"); 710 if (uoffsetIsU4) { 711 u4(offset); 712 } else { 713 u2(offset); 714 } 715 } 716 break; 717 } 718 } 719 } 720 } 721 } 722 723 public void writeCP() throws IOException { 724 int cpLen = cpoolSize; 725 u2(cpLen); 726 ByteArrayOutputStream buf = getAttrBuf(); 727 for (Element c : cpool.elements()) { 728 if (!c.isText()) { 729 System.out.println("## !isText " + c); 730 } 731 int id = (int) c.getAttrLong("id"); 732 int tag = cpTagValue(c.getName()); 733 String name = c.getText().toString(); 734 int pos; 735 u1(tag); 736 switch (tag) { 737 case CONSTANT_Utf8: { 738 int done = 0; 739 buf.reset(); 740 int nameLen = name.length(); 741 while (done < nameLen) { 742 int next = name.indexOf((char) 0, done); 743 if (next < 0) { 744 next = nameLen; 745 } 746 if (done < next) { 747 buf.write(name.substring(done, next).getBytes(UTF8_ENCODING)); 748 } 749 if (next < nameLen) { 750 buf.write(0300); 751 buf.write(0200); 752 next++; 753 } 754 done = next; 755 } 756 u2(buf.size()); 757 buf.writeTo(out); 758 } 759 break; 760 case CONSTANT_Integer: 761 u4(Integer.parseInt(name)); 762 break; 763 case CONSTANT_Float: 764 u4(Float.floatToIntBits(Float.parseFloat(name))); 765 break; 766 case CONSTANT_Long: 767 u8(Long.parseLong(name)); 768 //i += 1; // no need: extra cp slot is implicit 769 break; 770 case CONSTANT_Double: 771 u8(Double.doubleToLongBits(Double.parseDouble(name))); 772 //i += 1; // no need: extra cp slot is implicit 773 break; 774 case CONSTANT_Class: 775 case CONSTANT_String: 776 u2(getCPIndex(CONSTANT_Utf8, name)); 777 break; 778 case CONSTANT_Fieldref: 779 case CONSTANT_Methodref: 780 case CONSTANT_InterfaceMethodref: 781 pos = name.indexOf(' '); 782 u2(getCPIndex(CONSTANT_Class, name.substring(0, pos))); 783 u2(getCPIndex(CONSTANT_NameAndType, name.substring(pos + 1))); 784 break; 785 case CONSTANT_NameAndType: 786 pos = name.indexOf(' '); 787 u2(getCPIndex(CONSTANT_Utf8, name.substring(0, pos))); 788 u2(getCPIndex(CONSTANT_Utf8, name.substring(pos + 1))); 789 break; 790 } 791 } 792 putAttrBuf(buf); 793 } 794 795 public void cpRef(int tag, String name) throws IOException { 796 u2(getCPIndex(tag, name)); 797 } 798 799 public void u8(long x) throws IOException { 800 u4((int) (x >>> 32)); 801 u4((int) (x >>> 0)); 802 } 803 804 public void u4(int x) throws IOException { 805 u2(x >>> 16); 806 u2(x >>> 0); 807 } 808 809 public void u2(int x) throws IOException { 810 u1(x >>> 8); 811 u1(x >>> 0); 812 } 813 814 public void u1(int x) throws IOException { 815 out.write(x & 0xFF); 816 } 817 } 818