2668N/A/*
2668N/A * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2668N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
2668N/A *
2668N/A * This code is free software; you can redistribute it and/or modify it
2668N/A * under the terms of the GNU General Public License version 2 only, as
2668N/A * published by the Free Software Foundation. Oracle designates this
2668N/A * particular file as subject to the "Classpath" exception as provided
2668N/A * by Oracle in the LICENSE file that accompanied this code.
2668N/A *
2668N/A * This code is distributed in the hope that it will be useful, but WITHOUT
2668N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
2668N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
2668N/A * version 2 for more details (a copy is included in the LICENSE file that
2668N/A * accompanied this code).
2668N/A *
2668N/A * You should have received a copy of the GNU General Public License version
2668N/A * 2 along with this work; if not, write to the Free Software Foundation,
2668N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2668N/A *
2668N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2668N/A * or visit www.oracle.com if you need additional information or have any
2668N/A * questions.
2668N/A */
2668N/Apackage xmlkit; // -*- mode: java; indent-tabs-mode: nil -*-
2668N/A
2668N/Aimport java.util.*;
2668N/Aimport java.lang.reflect.*;
2668N/Aimport java.io.*;
2668N/Aimport xmlkit.XMLKit.Element;
2668N/A/*
2668N/A * @author jrose
2668N/A */
2668N/Apublic class ClassWriter extends ClassSyntax implements ClassSyntax.GetCPIndex {
2668N/A
2668N/A private static final CommandLineParser CLP = new CommandLineParser(""
2668N/A + "-source: +> = \n"
2668N/A + "-dest: +> = \n"
2668N/A + "-encoding: +> = \n"
2668N/A + "-parseBytes $ \n"
2668N/A + "- *? \n"
2668N/A + "\n");
2668N/A
2668N/A public static void main(String[] ava) throws IOException {
2668N/A ArrayList<String> av = new ArrayList<String>(Arrays.asList(ava));
2668N/A HashMap<String, String> props = new HashMap<String, String>();
2668N/A props.put("-encoding:", "UTF8"); // default
2668N/A CLP.parse(av, props);
2668N/A File source = asFile(props.get("-source:"));
2668N/A File dest = asFile(props.get("-dest:"));
2668N/A String encoding = props.get("-encoding:");
2668N/A boolean parseBytes = props.containsKey("-parseBytes");
2668N/A boolean destMade = false;
2668N/A
2668N/A for (String a : av) {
2668N/A File f;
2668N/A File inf = new File(source, a);
2668N/A System.out.println("Reading " + inf);
2668N/A Element e;
2668N/A if (inf.getName().endsWith(".class")) {
2668N/A ClassReader cr = new ClassReader();
2668N/A cr.parseBytes = parseBytes;
2668N/A e = cr.readFrom(inf);
2668N/A f = new File(a);
2668N/A } else if (inf.getName().endsWith(".xml")) {
2668N/A InputStream in = new FileInputStream(inf);
2668N/A Reader inw = ClassReader.makeReader(in, encoding);
2668N/A e = XMLKit.readFrom(inw);
2668N/A e.findAllInTree(XMLKit.and(XMLKit.elementFilter(nonAttrTags()),
2668N/A XMLKit.methodFilter(Element.method("trimText"))));
2668N/A //System.out.println(e);
2668N/A inw.close();
2668N/A f = new File(a.substring(0, a.length() - ".xml".length()) + ".class");
2668N/A } else {
2668N/A System.out.println("Warning: unknown input " + a);
2668N/A continue;
2668N/A }
2668N/A // Now write it:
2668N/A if (!destMade) {
2668N/A destMade = true;
2668N/A if (dest == null) {
2668N/A dest = File.createTempFile("TestOut", ".dir", new File("."));
2668N/A dest.delete();
2668N/A System.out.println("Writing results to " + dest);
2668N/A }
2668N/A if (!(dest.isDirectory() || dest.mkdir())) {
2668N/A throw new RuntimeException("Cannot create " + dest);
2668N/A }
2668N/A }
2668N/A File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath());
2668N/A outf.getParentFile().mkdirs();
2668N/A new ClassWriter(e).writeTo(outf);
2668N/A }
2668N/A }
2668N/A
2668N/A private static File asFile(String str) {
2668N/A return (str == null) ? null : new File(str);
2668N/A }
2668N/A
2668N/A public void writeTo(File file) throws IOException {
2668N/A OutputStream out = null;
2668N/A try {
2668N/A out = new BufferedOutputStream(new FileOutputStream(file));
2668N/A writeTo(out);
2668N/A } finally {
2668N/A if (out != null) {
2668N/A out.close();
2668N/A }
2668N/A }
2668N/A }
2668N/A protected String[] callables; // varies
2668N/A protected int cpoolSize = 0;
2668N/A protected HashMap<String, String> attrTypesByTag;
2668N/A protected OutputStream out;
2668N/A protected HashMap<String, int[]> cpMap = new HashMap<String, int[]>();
2668N/A protected ArrayList<ByteArrayOutputStream> attrBufs = new ArrayList<ByteArrayOutputStream>();
2668N/A
2668N/A private void setupAttrTypes() {
2668N/A attrTypesByTag = new HashMap<String, String>();
2668N/A for (String key : attrTypes.keySet()) {
2668N/A String pfx = key.substring(0, key.indexOf('.') + 1);
2668N/A String val = attrTypes.get(key);
2668N/A int pos = val.indexOf('<');
2668N/A if (pos >= 0) {
2668N/A String tag = val.substring(pos + 1, val.indexOf('>', pos));
2668N/A attrTypesByTag.put(pfx + tag, key);
2668N/A }
2668N/A }
2668N/A //System.out.println("attrTypesByTag: "+attrTypesByTag);
2668N/A }
2668N/A
2668N/A protected ByteArrayOutputStream getAttrBuf() {
2668N/A int nab = attrBufs.size();
2668N/A if (nab == 0) {
2668N/A return new ByteArrayOutputStream(1024);
2668N/A }
2668N/A ByteArrayOutputStream ab = attrBufs.get(nab - 1);
2668N/A attrBufs.remove(nab - 1);
2668N/A return ab;
2668N/A }
2668N/A
2668N/A protected void putAttrBuf(ByteArrayOutputStream ab) {
2668N/A ab.reset();
2668N/A attrBufs.add(ab);
2668N/A }
2668N/A
2668N/A public ClassWriter(Element root) {
2668N/A this(root, null);
2668N/A }
2668N/A
2668N/A public ClassWriter(Element root, ClassSyntax cr) {
2668N/A if (cr != null) {
2668N/A attrTypes = cr.attrTypes;
2668N/A }
2668N/A setupAttrTypes();
2668N/A if (root.getName() == "ClassFile") {
2668N/A cfile = root;
2668N/A cpool = root.findElement("ConstantPool");
2668N/A klass = root.findElement("Class");
2668N/A } else if (root.getName() == "Class") {
2668N/A cfile = new Element("ClassFile",
2668N/A new String[]{
2668N/A "magic", String.valueOf(0xCAFEBABE),
2668N/A "minver", "0", "majver", "46",});
2668N/A cpool = new Element("ConstantPool");
2668N/A klass = root;
2668N/A } else {
2668N/A throw new IllegalArgumentException("bad element type " + root.getName());
2668N/A }
2668N/A if (cpool == null) {
2668N/A cpool = new Element("ConstantPool");
2668N/A }
2668N/A
2668N/A int cpLen = 1 + cpool.size();
2668N/A for (Element c : cpool.elements()) {
2668N/A int id = (int) c.getAttrLong("id");
2668N/A int tag = cpTagValue(c.getName());
2668N/A setCPIndex(tag, c.getText().toString(), id);
2668N/A switch (tag) {
2668N/A case CONSTANT_Long:
2668N/A case CONSTANT_Double:
2668N/A cpLen += 1;
2668N/A }
2668N/A }
2668N/A cpoolSize = cpLen;
2668N/A }
2668N/A
2668N/A public int findCPIndex(int tag, String name) {
2668N/A if (name == null) {
2668N/A return 0;
2668N/A }
2668N/A int[] ids = cpMap.get(name.toString());
2668N/A return (ids == null) ? 0 : ids[tag];
2668N/A }
2668N/A
2668N/A public int getCPIndex(int tag, String name) {
2668N/A //System.out.println("getCPIndex "+cpTagName(tag)+" "+name);
2668N/A if (name == null) {
2668N/A return 0;
2668N/A }
2668N/A int id = findCPIndex(tag, name);
2668N/A if (id == 0) {
2668N/A id = cpoolSize;
2668N/A cpoolSize += 1;
2668N/A setCPIndex(tag, name, id);
2668N/A cpool.add(new Element(cpTagName(tag),
2668N/A new String[]{"id", "" + id},
2668N/A new Object[]{name}));
2668N/A int pos;
2668N/A switch (tag) {
2668N/A case CONSTANT_Long:
2668N/A case CONSTANT_Double:
2668N/A cpoolSize += 1;
2668N/A break;
2668N/A case CONSTANT_Class:
2668N/A case CONSTANT_String:
2668N/A getCPIndex(CONSTANT_Utf8, name);
2668N/A break;
2668N/A case CONSTANT_Fieldref:
2668N/A case CONSTANT_Methodref:
2668N/A case CONSTANT_InterfaceMethodref:
2668N/A pos = name.indexOf(' ');
2668N/A getCPIndex(CONSTANT_Class, name.substring(0, pos));
2668N/A getCPIndex(CONSTANT_NameAndType, name.substring(pos + 1));
2668N/A break;
2668N/A case CONSTANT_NameAndType:
2668N/A pos = name.indexOf(' ');
2668N/A getCPIndex(CONSTANT_Utf8, name.substring(0, pos));
2668N/A getCPIndex(CONSTANT_Utf8, name.substring(pos + 1));
2668N/A break;
2668N/A }
2668N/A }
2668N/A return id;
2668N/A }
2668N/A
2668N/A public void setCPIndex(int tag, String name, int id) {
2668N/A //System.out.println("setCPIndex id="+id+" tag="+tag+" name="+name);
2668N/A int[] ids = cpMap.get(name);
2668N/A if (ids == null) {
2668N/A cpMap.put(name, ids = new int[13]);
2668N/A }
2668N/A if (ids[tag] != 0 && ids[tag] != id) {
2668N/A System.out.println("Warning: Duplicate CP entries for " + ids[tag] + " and " + id);
2668N/A }
2668N/A //assert(ids[tag] == 0 || ids[tag] == id);
2668N/A ids[tag] = id;
2668N/A }
2668N/A
2668N/A public int parseFlags(String flagString) {
2668N/A int flags = 0;
2668N/A int i = -1;
2668N/A for (String[] names : modifierNames) {
2668N/A ++i;
2668N/A for (String name : names) {
2668N/A if (name == null) {
2668N/A continue;
2668N/A }
2668N/A int pos = flagString.indexOf(name);
2668N/A if (pos >= 0) {
2668N/A flags |= (1 << i);
2668N/A }
2668N/A }
2668N/A }
2668N/A return flags;
2668N/A }
2668N/A
2668N/A public void writeTo(OutputStream realOut) throws IOException {
2668N/A OutputStream headOut = realOut;
2668N/A ByteArrayOutputStream tailOut = new ByteArrayOutputStream();
2668N/A
2668N/A // write the body of the class file first
2668N/A this.out = tailOut;
2668N/A writeClass();
2668N/A
2668N/A // write the file header last
2668N/A this.out = headOut;
2668N/A u4((int) cfile.getAttrLong("magic"));
2668N/A u2((int) cfile.getAttrLong("minver"));
2668N/A u2((int) cfile.getAttrLong("majver"));
2668N/A writeCP();
2668N/A
2668N/A // recopy the file tail
2668N/A this.out = null;
2668N/A tailOut.writeTo(realOut);
2668N/A }
2668N/A
2668N/A void writeClass() throws IOException {
2668N/A int flags = parseFlags(klass.getAttr("flags"));
2668N/A flags ^= Modifier.SYNCHRONIZED;
2668N/A u2(flags);
2668N/A cpRef(CONSTANT_Class, klass.getAttr("name"));
2668N/A cpRef(CONSTANT_Class, klass.getAttr("super"));
2668N/A Element interfaces = klass.findAllElements("Interface");
2668N/A u2(interfaces.size());
2668N/A for (Element e : interfaces.elements()) {
2668N/A cpRef(CONSTANT_Class, e.getAttr("name"));
2668N/A }
2668N/A for (int isMethod = 0; isMethod <= 1; isMethod++) {
2668N/A Element members = klass.findAllElements(isMethod != 0 ? "Method" : "Field");
2668N/A u2(members.size());
2668N/A for (Element m : members.elements()) {
2668N/A writeMember(m, isMethod != 0);
2668N/A }
2668N/A }
2668N/A writeAttributesFor(klass);
2668N/A }
2668N/A
2668N/A private void writeMember(Element member, boolean isMethod) throws IOException {
2668N/A //System.out.println("writeMember "+member);
2668N/A u2(parseFlags(member.getAttr("flags")));
2668N/A cpRef(CONSTANT_Utf8, member.getAttr("name"));
2668N/A cpRef(CONSTANT_Utf8, member.getAttr("type"));
2668N/A writeAttributesFor(member);
2668N/A }
2668N/A
2668N/A protected void writeAttributesFor(Element x) throws IOException {
2668N/A LinkedHashSet<String> attrNames = new LinkedHashSet<String>();
2668N/A for (Element e : x.elements()) {
2668N/A attrNames.add(e.getName()); // uniquifying
2668N/A }
2668N/A attrNames.removeAll(nonAttrTags());
2668N/A u2(attrNames.size());
2668N/A if (attrNames.isEmpty()) {
2668N/A return;
2668N/A }
2668N/A Element prevCurrent;
2668N/A if (x.getName() == "Code") {
2668N/A prevCurrent = currentCode;
2668N/A currentCode = x;
2668N/A } else {
2668N/A prevCurrent = currentMember;
2668N/A currentMember = x;
2668N/A }
2668N/A OutputStream realOut = this.out;
2668N/A for (String utag : attrNames) {
2668N/A String qtag = x.getName() + "." + utag;
2668N/A String wtag = "*." + utag;
2668N/A String key = attrTypesByTag.get(qtag);
2668N/A if (key == null) {
2668N/A key = attrTypesByTag.get(wtag);
2668N/A }
2668N/A String type = attrTypes.get(key);
2668N/A //System.out.println("tag "+qtag+" => key "+key+"; type "+type);
2668N/A Element attrs = x.findAllElements(utag);
2668N/A ByteArrayOutputStream attrBuf = getAttrBuf();
2668N/A if (type == null) {
2668N/A if (attrs.size() != 1 || !attrs.get(0).equals(new Element(utag))) {
2668N/A System.out.println("Warning: No attribute type description: " + qtag);
2668N/A }
2668N/A key = wtag;
2668N/A } else {
2668N/A try {
2668N/A this.out = attrBuf;
2668N/A // unparse according to type desc.
2668N/A if (type.equals("<Code>...")) {
2668N/A writeCode((Element) attrs.get(0)); // assume only 1
2668N/A } else if (type.equals("<Frame>...")) {
2668N/A writeStackMap(attrs, false);
2668N/A } else if (type.equals("<FrameX>...")) {
2668N/A writeStackMap(attrs, true);
2668N/A } else if (type.startsWith("[")) {
2668N/A writeAttributeRecursive(attrs, type);
2668N/A } else {
2668N/A writeAttribute(attrs, type);
2668N/A }
2668N/A } finally {
2668N/A //System.out.println("Attr Bytes = \""+attrBuf.toString(EIGHT_BIT_CHAR_ENCODING).replace('"', (char)('"'|0x80))+"\"");
2668N/A this.out = realOut;
2668N/A }
2668N/A }
2668N/A cpRef(CONSTANT_Utf8, key.substring(key.indexOf('.') + 1));
2668N/A u4(attrBuf.size());
2668N/A attrBuf.writeTo(out);
2668N/A putAttrBuf(attrBuf);
2668N/A }
2668N/A if (x.getName() == "Code") {
2668N/A currentCode = prevCurrent;
2668N/A } else {
2668N/A currentMember = prevCurrent;
2668N/A }
2668N/A }
2668N/A
2668N/A private void writeAttributeRecursive(Element aval, String type) throws IOException {
2668N/A assert (callables == null);
2668N/A callables = getBodies(type);
2668N/A writeAttribute(aval, callables[0]);
2668N/A callables = null;
2668N/A }
2668N/A
2668N/A private void writeAttribute(Element aval, String type) throws IOException {
2668N/A //System.out.println("writeAttribute "+aval+" using "+type);
2668N/A String nextAttrName = null;
2668N/A boolean afterElemHead = false;
2668N/A for (int len = type.length(), next, i = 0; i < len; i = next) {
2668N/A int value;
2668N/A char intKind;
2668N/A int tag;
2668N/A int sigChar;
2668N/A String attrValue;
2668N/A switch (type.charAt(i)) {
2668N/A case '<':
2668N/A assert (nextAttrName == null);
2668N/A next = type.indexOf('>', i);
2668N/A String form = type.substring(i + 1, next++);
2668N/A if (form.indexOf('=') < 0) {
2668N/A // elem_placement = '<' elemname '>'
2668N/A if (aval.isAnonymous()) {
2668N/A assert (aval.size() == 1);
2668N/A aval = (Element) aval.get(0);
2668N/A }
2668N/A assert (aval.getName().equals(form)) : aval + " // " + form;
2668N/A afterElemHead = true;
2668N/A } else {
2668N/A // attr_placement = '(' attrname '=' (value)? ')'
2668N/A int eqPos = form.indexOf('=');
2668N/A assert (eqPos >= 0);
2668N/A nextAttrName = form.substring(0, eqPos).intern();
2668N/A if (eqPos != form.length() - 1) {
2668N/A // value is implicit, not placed in file
2668N/A nextAttrName = null;
2668N/A }
2668N/A afterElemHead = false;
2668N/A }
2668N/A continue;
2668N/A case '(':
2668N/A next = type.indexOf(')', ++i);
2668N/A int callee = Integer.parseInt(type.substring(i, next++));
2668N/A writeAttribute(aval, callables[callee]);
2668N/A continue;
2668N/A case 'N': // replication = 'N' int '[' type ... ']'
2668N/A {
2668N/A assert (nextAttrName == null);
2668N/A afterElemHead = false;
2668N/A char countType = type.charAt(i + 1);
2668N/A next = i + 2;
2668N/A String type1 = getBody(type, next);
2668N/A Element elems = aval;
2668N/A if (type1.startsWith("<")) {
2668N/A // Select only matching members of aval.
2668N/A String elemName = type1.substring(1, type1.indexOf('>'));
2668N/A elems = aval.findAllElements(elemName);
2668N/A }
2668N/A putInt(elems.size(), countType);
2668N/A next += type1.length() + 2; // skip body and brackets
2668N/A for (Element elem : elems.elements()) {
2668N/A writeAttribute(elem, type1);
2668N/A }
2668N/A }
2668N/A continue;
2668N/A case 'T': // union = 'T' any_int union_case* '(' ')' '[' body ']'
2668N/A // write the value
2668N/A value = (int) aval.getAttrLong("tag");
2668N/A assert (aval.getAttr("tag") != null) : aval;
2668N/A intKind = type.charAt(++i);
2668N/A if (intKind == 'S') {
2668N/A intKind = type.charAt(++i);
2668N/A }
2668N/A putInt(value, intKind);
2668N/A nextAttrName = null;
2668N/A afterElemHead = false;
2668N/A ++i; // skip the int type char
2668N/A // union_case = '(' ('-')? digit+ ')' '[' body ']'
2668N/A for (boolean foundCase = false;;) {
2668N/A assert (type.charAt(i) == '(');
2668N/A next = type.indexOf(')', ++i);
2668N/A assert (next >= i);
2668N/A String caseStr = type.substring(i, next++);
2668N/A String type1 = getBody(type, next);
2668N/A next += type1.length() + 2; // skip body and brackets
2668N/A boolean lastCase = (caseStr.length() == 0);
2668N/A if (!foundCase
2668N/A && (lastCase || matchTag(value, caseStr))) {
2668N/A foundCase = true;
2668N/A // Execute this body.
2668N/A writeAttribute(aval, type1);
2668N/A }
2668N/A if (lastCase) {
2668N/A break;
2668N/A }
2668N/A }
2668N/A continue;
2668N/A case 'B':
2668N/A case 'H':
2668N/A case 'I': // int = oneof "BHI"
2668N/A value = (int) aval.getAttrLong(nextAttrName);
2668N/A intKind = type.charAt(i);
2668N/A next = i + 1;
2668N/A break;
2668N/A case 'K':
2668N/A sigChar = type.charAt(i + 1);
2668N/A if (sigChar == 'Q') {
2668N/A assert (currentMember.getName() == "Field");
2668N/A assert (aval.getName() == "ConstantValue");
2668N/A String sig = currentMember.getAttr("type");
2668N/A sigChar = sig.charAt(0);
2668N/A switch (sigChar) {
2668N/A case 'Z':
2668N/A case 'B':
2668N/A case 'C':
2668N/A case 'S':
2668N/A sigChar = 'I';
2668N/A break;
2668N/A }
2668N/A }
2668N/A switch (sigChar) {
2668N/A case 'I':
2668N/A tag = CONSTANT_Integer;
2668N/A break;
2668N/A case 'J':
2668N/A tag = CONSTANT_Long;
2668N/A break;
2668N/A case 'F':
2668N/A tag = CONSTANT_Float;
2668N/A break;
2668N/A case 'D':
2668N/A tag = CONSTANT_Double;
2668N/A break;
2668N/A case 'L':
2668N/A tag = CONSTANT_String;
2668N/A break;
2668N/A default:
2668N/A assert (false);
2668N/A tag = 0;
2668N/A }
2668N/A assert (type.charAt(i + 2) == 'H'); // only H works for now
2668N/A next = i + 3;
2668N/A assert (afterElemHead || nextAttrName != null);
2668N/A //System.out.println("get attr "+nextAttrName+" in "+aval);
2668N/A if (nextAttrName != null) {
2668N/A attrValue = aval.getAttr(nextAttrName);
2668N/A assert (attrValue != null);
2668N/A } else {
2668N/A assert (aval.isText()) : aval;
2668N/A attrValue = aval.getText().toString();
2668N/A }
2668N/A value = getCPIndex(tag, attrValue);
2668N/A intKind = 'H'; //type.charAt(i+2);
2668N/A break;
2668N/A case 'R':
2668N/A sigChar = type.charAt(i + 1);
2668N/A switch (sigChar) {
2668N/A case 'C':
2668N/A tag = CONSTANT_Class;
2668N/A break;
2668N/A case 'S':
2668N/A tag = CONSTANT_Utf8;
2668N/A break;
2668N/A case 'D':
2668N/A tag = CONSTANT_Class;
2668N/A break;
2668N/A case 'F':
2668N/A tag = CONSTANT_Fieldref;
2668N/A break;
2668N/A case 'M':
2668N/A tag = CONSTANT_Methodref;
2668N/A break;
2668N/A case 'I':
2668N/A tag = CONSTANT_InterfaceMethodref;
2668N/A break;
2668N/A case 'U':
2668N/A tag = CONSTANT_Utf8;
2668N/A break;
2668N/A //case 'Q': tag = CONSTANT_Class; break;
2668N/A default:
2668N/A assert (false);
2668N/A tag = 0;
2668N/A }
2668N/A assert (type.charAt(i + 2) == 'H'); // only H works for now
2668N/A next = i + 3;
2668N/A assert (afterElemHead || nextAttrName != null);
2668N/A //System.out.println("get attr "+nextAttrName+" in "+aval);
2668N/A if (nextAttrName != null) {
2668N/A attrValue = aval.getAttr(nextAttrName);
2668N/A } else if (aval.hasText()) {
2668N/A attrValue = aval.getText().toString();
2668N/A } else {
2668N/A attrValue = null;
2668N/A }
2668N/A value = getCPIndex(tag, attrValue);
2668N/A intKind = 'H'; //type.charAt(i+2);
2668N/A break;
2668N/A case 'P': // bci = 'P' int
2668N/A case 'S': // signed_int = 'S' int
2668N/A next = i + 2;
2668N/A value = (int) aval.getAttrLong(nextAttrName);
2668N/A intKind = type.charAt(i + 1);
2668N/A break;
2668N/A case 'F':
2668N/A next = i + 2;
2668N/A value = parseFlags(aval.getAttr(nextAttrName));
2668N/A intKind = type.charAt(i + 1);
2668N/A break;
2668N/A default:
2668N/A throw new RuntimeException("bad attr format '" + type.charAt(i) + "': " + type);
2668N/A }
2668N/A // write the value
2668N/A putInt(value, intKind);
2668N/A nextAttrName = null;
2668N/A afterElemHead = false;
2668N/A }
2668N/A assert (nextAttrName == null);
2668N/A }
2668N/A
2668N/A private void putInt(int x, char ch) throws IOException {
2668N/A switch (ch) {
2668N/A case 'B':
2668N/A u1(x);
2668N/A break;
2668N/A case 'H':
2668N/A u2(x);
2668N/A break;
2668N/A case 'I':
2668N/A u4(x);
2668N/A break;
2668N/A }
2668N/A assert ("BHI".indexOf(ch) >= 0);
2668N/A }
2668N/A
2668N/A private void writeCode(Element code) throws IOException {
2668N/A //System.out.println("writeCode "+code);
2668N/A //Element m = new Element(currentMember); m.remove(code);
2668N/A //System.out.println(" in "+m);
2668N/A int stack = (int) code.getAttrLong("stack");
2668N/A int local = (int) code.getAttrLong("local");
2668N/A Element bytes = code.findElement("Bytes");
2668N/A Element insns = code.findElement("Instructions");
2668N/A String bytecodes;
2668N/A if (insns == null) {
2668N/A bytecodes = bytes.getText().toString();
2668N/A } else {
2668N/A bytecodes = InstructionSyntax.assemble(insns, this);
2668N/A // Cache the assembled bytecodes:
2668N/A bytes = new Element("Bytes", (String[]) null, bytecodes);
2668N/A code.add(0, bytes);
2668N/A }
2668N/A u2(stack);
2668N/A u2(local);
2668N/A int length = bytecodes.length();
2668N/A u4(length);
2668N/A for (int i = 0; i < length; i++) {
2668N/A u1((byte) bytecodes.charAt(i));
2668N/A }
2668N/A Element handlers = code.findAllElements("Handler");
2668N/A u2(handlers.size());
2668N/A for (Element handler : handlers.elements()) {
2668N/A int start = (int) handler.getAttrLong("start");
2668N/A int end = (int) handler.getAttrLong("end");
2668N/A int catsh = (int) handler.getAttrLong("catch");
2668N/A u2(start);
2668N/A u2(end);
2668N/A u2(catsh);
2668N/A cpRef(CONSTANT_Class, handler.getAttr("class"));
2668N/A }
2668N/A writeAttributesFor(code);
2668N/A }
2668N/A
2668N/A protected void writeStackMap(Element attrs, boolean hasXOption) throws IOException {
2668N/A Element bytes = currentCode.findElement("Bytes");
2668N/A assert (bytes != null && bytes.size() == 1);
2668N/A int byteLength = ((String) bytes.get(0)).length();
2668N/A boolean uoffsetIsU4 = (byteLength >= (1 << 16));
2668N/A boolean ulocalvarIsU4 = currentCode.getAttrLong("local") >= (1 << 16);
2668N/A boolean ustackIsU4 = currentCode.getAttrLong("stack") >= (1 << 16);
2668N/A if (uoffsetIsU4) {
2668N/A u4(attrs.size());
2668N/A } else {
2668N/A u2(attrs.size());
2668N/A }
2668N/A for (Element frame : attrs.elements()) {
2668N/A int bci = (int) frame.getAttrLong("bci");
2668N/A if (uoffsetIsU4) {
2668N/A u4(bci);
2668N/A } else {
2668N/A u2(bci);
2668N/A }
2668N/A if (hasXOption) {
2668N/A u1((int) frame.getAttrLong("flags"));
2668N/A }
2668N/A // Scan local and stack types in this frame:
2668N/A final int LOCALS = 0, STACK = 1;
2668N/A for (int j = LOCALS; j <= STACK; j++) {
2668N/A Element types = frame.findElement(j == LOCALS ? "Local" : "Stack");
2668N/A int typeSize = (types == null) ? 0 : types.size();
2668N/A if (j == LOCALS) {
2668N/A if (ulocalvarIsU4) {
2668N/A u4(typeSize);
2668N/A } else {
2668N/A u2(typeSize);
2668N/A }
2668N/A } else { // STACK
2668N/A if (ustackIsU4) {
2668N/A u4(typeSize);
2668N/A } else {
2668N/A u2(typeSize);
2668N/A }
2668N/A }
2668N/A if (types == null) {
2668N/A continue;
2668N/A }
2668N/A for (Element type : types.elements()) {
2668N/A int tag = itemTagValue(type.getName());
2668N/A u1(tag);
2668N/A switch (tag) {
2668N/A case ITEM_Object:
2668N/A cpRef(CONSTANT_Class, type.getAttr("class"));
2668N/A break;
2668N/A case ITEM_Uninitialized:
2668N/A case ITEM_ReturnAddress: {
2668N/A int offset = (int) type.getAttrLong("bci");
2668N/A if (uoffsetIsU4) {
2668N/A u4(offset);
2668N/A } else {
2668N/A u2(offset);
2668N/A }
2668N/A }
2668N/A break;
2668N/A }
2668N/A }
2668N/A }
2668N/A }
2668N/A }
2668N/A
2668N/A public void writeCP() throws IOException {
2668N/A int cpLen = cpoolSize;
2668N/A u2(cpLen);
2668N/A ByteArrayOutputStream buf = getAttrBuf();
2668N/A for (Element c : cpool.elements()) {
2668N/A if (!c.isText()) {
2668N/A System.out.println("## !isText " + c);
2668N/A }
2668N/A int id = (int) c.getAttrLong("id");
2668N/A int tag = cpTagValue(c.getName());
2668N/A String name = c.getText().toString();
2668N/A int pos;
2668N/A u1(tag);
2668N/A switch (tag) {
2668N/A case CONSTANT_Utf8: {
2668N/A int done = 0;
2668N/A buf.reset();
2668N/A int nameLen = name.length();
2668N/A while (done < nameLen) {
2668N/A int next = name.indexOf((char) 0, done);
2668N/A if (next < 0) {
2668N/A next = nameLen;
2668N/A }
2668N/A if (done < next) {
2668N/A buf.write(name.substring(done, next).getBytes(UTF8_ENCODING));
2668N/A }
2668N/A if (next < nameLen) {
2668N/A buf.write(0300);
2668N/A buf.write(0200);
2668N/A next++;
2668N/A }
2668N/A done = next;
2668N/A }
2668N/A u2(buf.size());
2668N/A buf.writeTo(out);
2668N/A }
2668N/A break;
2668N/A case CONSTANT_Integer:
2668N/A u4(Integer.parseInt(name));
2668N/A break;
2668N/A case CONSTANT_Float:
2668N/A u4(Float.floatToIntBits(Float.parseFloat(name)));
2668N/A break;
2668N/A case CONSTANT_Long:
2668N/A u8(Long.parseLong(name));
2668N/A //i += 1; // no need: extra cp slot is implicit
2668N/A break;
2668N/A case CONSTANT_Double:
2668N/A u8(Double.doubleToLongBits(Double.parseDouble(name)));
2668N/A //i += 1; // no need: extra cp slot is implicit
2668N/A break;
2668N/A case CONSTANT_Class:
2668N/A case CONSTANT_String:
2668N/A u2(getCPIndex(CONSTANT_Utf8, name));
2668N/A break;
2668N/A case CONSTANT_Fieldref:
2668N/A case CONSTANT_Methodref:
2668N/A case CONSTANT_InterfaceMethodref:
2668N/A pos = name.indexOf(' ');
2668N/A u2(getCPIndex(CONSTANT_Class, name.substring(0, pos)));
2668N/A u2(getCPIndex(CONSTANT_NameAndType, name.substring(pos + 1)));
2668N/A break;
2668N/A case CONSTANT_NameAndType:
2668N/A pos = name.indexOf(' ');
2668N/A u2(getCPIndex(CONSTANT_Utf8, name.substring(0, pos)));
2668N/A u2(getCPIndex(CONSTANT_Utf8, name.substring(pos + 1)));
2668N/A break;
2668N/A }
2668N/A }
2668N/A putAttrBuf(buf);
2668N/A }
2668N/A
2668N/A public void cpRef(int tag, String name) throws IOException {
2668N/A u2(getCPIndex(tag, name));
2668N/A }
2668N/A
2668N/A public void u8(long x) throws IOException {
2668N/A u4((int) (x >>> 32));
2668N/A u4((int) (x >>> 0));
2668N/A }
2668N/A
2668N/A public void u4(int x) throws IOException {
2668N/A u2(x >>> 16);
2668N/A u2(x >>> 0);
2668N/A }
2668N/A
2668N/A public void u2(int x) throws IOException {
2668N/A u1(x >>> 8);
2668N/A u1(x >>> 0);
2668N/A }
2668N/A
2668N/A public void u1(int x) throws IOException {
2668N/A out.write(x & 0xFF);
2668N/A }
2668N/A}
2668N/A