jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java
changeset 12646 fa5227d43363
parent 12645 e0d32945f6ab
parent 12579 4cc5610a6dd6
child 12647 f9991bc4fdde
equal deleted inserted replaced
12645:e0d32945f6ab 12646:fa5227d43363
     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