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