/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.proxy.compiler; import java.lang.reflect.*; import java.util.*; import java.io.*; /** * A simple bytecode assembler. * * @deprecated Use {@link ProxyCompiler} or Jakarta BCEL instead. * * @author Unknown * @version $Revision: 81030 $ */ public class ProxyAssembler { // constant pool: Vector cv = new Vector(); Hashtable ct = new Hashtable(); Hashtable ut = new Hashtable(); short cn = 1; // members: Vector members; AMember current; ByteArrayOutputStream code; // current.code Stack stack; // current.stack // other info: String className; int modifiers; Class superClass; Class interfaces[]; public short getIndex(Object x) { Object n = ct.get(x); if (n == null) { n = new Short(cn++); ct.put(x, n); cv.addElement(x); } return ((Short)n).shortValue(); } public short getUtfIndex(String x) { Object n = ut.get(x); if (n == null) { n = new Short(cn++); ut.put(x, n); int xlen = 2 + x.length(); // x.utfLength(), really ByteArrayOutputStream bytes = new ByteArrayOutputStream(xlen); DataOutputStream ds = new DataOutputStream(bytes); try { ds.writeByte(CONSTANT_UTF8); ds.writeUTF(x); } catch (IOException ee) { throw new RuntimeException(ee.toString()); } cv.addElement(bytes.toByteArray()); } return ((Short)n).shortValue(); } public short getNTIndex(String name, String sig) { NameAndType nt = new NameAndType(); nt.name = getUtfIndex(name); nt.sig = getUtfIndex(sig); return getIndex(nt); } public short getClassIndex( Class c ) { short ci = getUtfIndex(c.getName().replace('.', '/')); short data[] = { CONSTANT_CLASS, ci }; return getIndex( data ); } public short getMemberIndex(Object cls, String name, Class ptypes[]) { if (cls instanceof Class) { Class c = (Class) cls; Member m; try { if (ptypes == null) { m = c.getField(name); } else if (name.equals("<init>")) { m = c.getConstructor(ptypes); } else { m = c.getMethod(name, ptypes); } } catch (NoSuchMethodException ee) { throw new IllegalArgumentException(ee+" in "+c); } catch (NoSuchFieldException ee) { throw new IllegalArgumentException(ee+" in "+c); } return getIndex(m); } else if (cls instanceof ProxyAssembler) { ProxyAssembler asm = (ProxyAssembler) cls; String sig = getSig(null, ptypes); AMember m = asm.findMember(sig, name); if (m == null) { throw new IllegalArgumentException(sig + " " + name); } return getIndex(m); } else { throw new IllegalArgumentException("not a type: "+cls); } } public short getMemberIndex(Object cls, String name) { return getMemberIndex(cls, name, null); } public static String getSig(Class t) { if (t == null) { return ""; } else if (t.isPrimitive()) { if (false) { return ""; } else if (t == Boolean.TYPE) { return "Z"; } else if (t == Character.TYPE) { return "C"; } else if (t == Byte.TYPE) { return "B"; } else if (t == Short.TYPE) { return "S"; } else if (t == Integer.TYPE) { return "I"; } else if (t == Long.TYPE) { return "J"; } else if (t == Float.TYPE) { return "F"; } else if (t == Double.TYPE) { return "D"; } else if (t == Void.TYPE) { return "V"; } else { Class a = java.lang.reflect.Array.newInstance(t, 0).getClass(); return getSig(a).substring(1); } } else if (t.isArray()) { return t.getName().replace('.', '/'); } else { return "L" + t.getName().replace('.', '/') + ";"; } } public static String getSig(Class rt, Class pt[]) { if (pt == null) { return getSig(rt); } StringBuffer sb = new StringBuffer(); sb.append("("); for (int i = 0; i < pt.length; i++) { sb.append(getSig(pt[i])); } sb.append(")"); sb.append(getSig(rt)); return sb.toString(); } boolean isInterface() { return Modifier.isInterface(modifiers); } public ProxyAssembler(String className, int modifiers, Class superClass, Class interfaces[]) { if (interfaces == null) interfaces = new Class[0]; this.className = className; this.modifiers = modifiers; this.superClass = superClass; this.interfaces = interfaces; cv.addElement(null); // the first cpool entry is unused members = new Vector(); addMember(0, "", null, "" ); } private static class AMember { int mods; int sp; int spmax; int locmax; int index; Class type; // field or method return type String sig; String name; Vector attr; Stack stack; ByteArrayOutputStream code; ProxyAssembler asm; } private static class Attr { String name; Object data; } private static class AValue { // found in the stack int num; Object type; } private static class NameAndType { short name; short sig; // must act as a hashtable key: public boolean equals(Object x) { if (x instanceof NameAndType) { NameAndType that = (NameAndType)x; return that.name == name && that.sig == sig; } return false; } public int hashCode() { return name + sig * 1000; } } public Object getCurrentMember() { return current; } public void setCurrentMember(Object m) { if (m == null) { m = members.elementAt(0); } current = (AMember) m; code = current.code; stack = current.stack; } AMember findMember(String sig, String name) { for (int i = 0; i < members.size(); i++) { AMember m = (AMember) members.elementAt(i); if (m.name.equals(name)) { if (!sig.startsWith("(") ? !m.sig.startsWith("(") : m.sig.startsWith(sig)) { return m; } } } return null; } void addExceptionAttribute( Class[] classes ) { try { if ((classes == null) || (classes.length == 0)) return; ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream( baos ); short count = (short) classes.length; dos.writeShort( count ); for (int iter=0; iter<classes.length; iter++) { dos.writeShort( getClassIndex(classes[iter]) ); } dos.flush(); baos.flush(); addAttribute( "Exception", baos.toByteArray() ); } catch (IOException cantHappen) { cantHappen.printStackTrace(); } } AMember addMember(int mods, String sig, Class[] exceptionClasses, String name) { String qsig = sig.substring(0, 1 + sig.indexOf(')')); AMember m = findMember(qsig, name); if (m != null) { setCurrentMember(m); current.mods |= mods; modifiers |= (mods & Modifier.ABSTRACT); return m; } m = new AMember(); m.asm = this; if (isMethodSig(sig)) { m.code = new ByteArrayOutputStream(); m.stack = new Stack(); } m.sig = sig; m.name = name; m.attr = new Vector(); m.index = members.size(); m.mods = mods; members.addElement(m); setCurrentMember(m); this.addExceptionAttribute( exceptionClasses ); return m; } public Object addMember(int mods, Class rtype, String name, Class ptypes[], Class[] exceptionClasses) { AMember m = addMember(mods, getSig(rtype, ptypes), exceptionClasses, name); if (ptypes != null && stack.size() == 0) { // push the arguments onto the stack if (!Modifier.isStatic(mods)) { declare(this); } for (int i = 0; i < ptypes.length; i++) { declare(ptypes[i]); } } m.type = rtype; this.addExceptionAttribute( exceptionClasses ); return m; } public Object addMember(int mods, Class type, Class[] exceptionClasses, String name ) { return addMember(mods, type, name, null, exceptionClasses); } public void addAttribute(AMember m, String name, Object data) { if (m == null) { m = (AMember) members.elementAt(0); } Attr a = new Attr(); a.name = name; a.data = data; m.attr.addElement(a); } public void addAttribute(String name, Object data) { addAttribute(current, name, data); } // instruction emitters private final static int opc_iconst_0 = 3, opc_bipush = 16, opc_sipush = 17, opc_ldc = 18, opc_ldc_w = 19, opc_ldc2_w = 20, opc_aaload = 50, opc_aastore = 83, opc_dup = 89, opc_getfield = 180, field_put = 1, field_static = -2, opc_invokevirtual = 182, opc_invokespecial = 183, opc_invokestatic = 184, opc_invokeinterface = 185, opc_new = 187, opc_newarray = 188, opc_anewarray = 189, opc_aload = 25, opc_aload_0 = 42, opc_wide = 196, opc_areturn = 176, opc_return = 177, opc_checkcast = 192, kind_a = 0, kind_i = -4, kind_l = -3, kind_f = -2, kind_d = -1, kind_b = 1, kind_c = 2, kind_s = 3; public int declare(Object t) { int n = current.sp; current.sp += 1; if (t == Double.TYPE || t == Long.TYPE) { current.sp += 1; } if (current.spmax < current.sp) { current.spmax = current.sp; } AValue se = new AValue(); se.num = n; se.type = t; stack.push(se); return stack.size() - 1; } public void undeclare(Object t) { AValue se = (AValue) stack.pop(); current.sp = se.num; } public void pushConstant(Object x) { int op = opc_ldc_w; if (x instanceof Integer) { declare(Integer.TYPE); int v = ((Integer)x).intValue(); if (v >= -1 && v <= 5) { code.write(opc_iconst_0 + v); return; } else if ((v > -(1 << 7)) && (v < (1 << 7))) { code.write(opc_bipush); code.write(v); return; } else if ((v > -(1 << 15)) && (v < (1 << 15))) { code.write(opc_sipush); codeShort(v); return; } } else if (x instanceof Float) { declare(Float.TYPE); } else if (x instanceof String) { declare(String.class); } else if (x instanceof Long) { declare(Long.TYPE); op = opc_ldc2_w; } else if (x instanceof Double) { declare(Double.TYPE); op = opc_ldc2_w; } else { throw new RuntimeException("unexpected: "+x); } int xi = getIndex(x); if (op == opc_ldc_w && xi < (1 << 8)) { code.write(opc_ldc); code.write(xi); } else { code.write(op); codeShort(xi); } } public void pushConstant(int x) { pushConstant(new Integer(x)); } public int pushLocal(int loc) { if (current.locmax < loc) { current.locmax = loc; } AValue se = (AValue) stack.elementAt(loc); int kind = typeKind(se.type, false); if (se.num <= 3) { code.write(opc_aload_0 + (kind * 4) + se.num); } else { codeWide(opc_aload + kind, se.num); } return declare(se.type); } public int dup() { code.write(opc_dup); return declare(stack.peek()); } public void checkCast(Object t) { code.write(opc_checkcast); codeShort(getIndex(t)); AValue se = (AValue) stack.pop(); if (se.type instanceof Class && ((Class)se.type).isPrimitive()) { undeclare(Object.class); // get an error declare(t); } se.type = t; } public int pushNewArray(Object etype) { int kind = typeKind(etype, true); int tcode; Class t; switch (kind) { case kind_a: code.write(opc_anewarray); codeShort(getIndex(etype)); return declare(Object[].class); case kind_f: tcode = 0x00000006; t = float[].class; break; case kind_d: tcode = 0x00000007; t = double[].class; break; case kind_i: tcode = 0x0000000a; t = int[].class; break; case kind_l: tcode = 0x0000000b; t = long[].class; break; case kind_b: if (etype == Boolean.TYPE) { tcode = 0x00000004; t = boolean[].class; } else { tcode = 0x00000008; t = byte[].class; } break; case kind_c: tcode = 0x00000005; t = char[].class; break; case kind_s: tcode = 0x00000009; t = short[].class; break; default: return 0; } code.write(opc_newarray); code.write(tcode); return declare(t); // etype[] } public void setElement(Object etype) { int kind = typeKind(etype, true); code.write(opc_aastore + kind); undeclare(etype); undeclare(Integer.TYPE); undeclare(null); // etype[] } public void pushElement(Object etype) { int kind = typeKind(etype, true); code.write(opc_aaload + kind); undeclare(Integer.TYPE); undeclare(null); // etype[] declare(etype); } public void ret() { if (current.sig.endsWith("V")) { code.write(opc_return); return; } Object t = current.type; undeclare(t); code.write(opc_areturn + typeKind(t, false)); stack = null; } private int dofield(Object cls, String name, boolean isPut) { int fi = getMemberIndex(cls, name); Object x = cv.elementAt(fi); int op = opc_getfield; int mod; Object t; if (x instanceof Field) { Field f = (Field) x; mod = f.getModifiers(); t = f.getType(); } else { AMember m = (AMember) x; mod = m.mods; t = m.type; } if (isPut) { op += field_put; undeclare(t); } if (Modifier.isStatic(mod)) { op += field_static; } else { undeclare(cls); } code.write(op); codeShort(fi); return isPut ? -1 : declare(t); } public int pushField(Object cls, String name) { return dofield(cls, name, false); } public void setField(Object cls, String name) { dofield(cls, name, true); } public int invoke(Object cls, String name, Class ptypes[]) { int mi = getMemberIndex(cls, name, ptypes); Object x = cv.elementAt(mi); int mod; Object rtype; int op = opc_invokevirtual; if (x instanceof Method) { Method m = (Method)x; mod = m.getModifiers(); rtype = m.getReturnType(); if (m.getDeclaringClass().isInterface()) { op = opc_invokeinterface; } } else if (x instanceof Constructor) { Constructor m = (Constructor)x; mod = m.getModifiers(); rtype = Void.TYPE; op = opc_invokespecial; } else { AMember m = (AMember) x; mod = m.mods; rtype = m.type; if (m.asm.isInterface()) { op = opc_invokeinterface; } } if (Modifier.isStatic(mod)) { op = opc_invokestatic; } else { undeclare(cls); } for (int i = ptypes.length; --i >= 0; ) { undeclare(ptypes[i]); } code.write(op); codeShort(mi); return declare(rtype); } private int typeKind(Object t, boolean subwords) { if (t != null && t instanceof Class && ((Class)t).isPrimitive()) { if (t == Float.TYPE) { return kind_f; } else if (t == Long.TYPE) { return kind_l; } else if (t == Double.TYPE) { return kind_d; } else if (t == Integer.TYPE || !subwords) { return kind_i; } else if (t == Character.TYPE) { return kind_c; } else if (t == Short.TYPE) { return kind_s; } else { return kind_b; } } else { return kind_a; } } private void codeWide(int op, int n) { if (n < (1 << 8)) { code.write(op); code.write(n); } else { code.write(opc_wide); code.write(op); codeShort(n); } } private void codeShort(int v) { code.write(v >>> 8); code.write(v); } private static boolean isMethodSig(String sig) { return (sig.indexOf('(') >= 0); } public byte[] getCode() { try { return internalGetCode(); } catch (IOException ee) { throw new RuntimeException(ee.toString()); } } public byte[] internalGetCode() throws IOException { // first, flush out all references to the cpool getIndex(this); getIndex(superClass); for (int i = 0; i < interfaces.length; i++) { getIndex(interfaces[i]); } int nfields = 0; int nmethods = 0; for (int i = 0; i < members.size(); i++) { AMember m = (AMember) members.elementAt(i); if (m.code != null) { byte[] codeAttr = getMethodCode(m, m.code); if (codeAttr != null) { addAttribute(m, "Code", codeAttr); } } for (int j = 0; j < m.attr.size(); j++) { Attr a = (Attr) m.attr.elementAt(j); getUtfIndex(a.name); } if (m.name.length() == 0) { continue; } getUtfIndex(m.name); getUtfIndex(m.sig); if (isMethodSig(m.sig)) { nmethods += 1; } else { nfields += 1; } } // next, deal with internal references in the cpool for (int i = 0; i < cv.size(); i++) { Object x = cv.elementAt(i); if (x == null) { continue; } else if (x instanceof String) { String s = (String)x; short si = getUtfIndex(s); short data[] = { CONSTANT_STRING, si }; x = data; } else if (x instanceof Class) { Class c = (Class)x; short ci = getUtfIndex(c.getName().replace('.', '/')); short data[] = { CONSTANT_CLASS, ci }; x = data; } else if (x instanceof Field) { Field m = (Field)x; short ci = getIndex(m.getDeclaringClass()); short nt = getNTIndex(m.getName(), getSig(m.getType())); short data[] = { CONSTANT_FIELD, ci, nt }; x = data; } else if (x instanceof Constructor) { Constructor m = (Constructor)x; short ci = getIndex(m.getDeclaringClass()); short nt = getNTIndex("<init>", getSig(Void.TYPE, m.getParameterTypes())); short data[] = { CONSTANT_METHOD, ci, nt }; x = data; } else if (x instanceof Method) { Method m = (Method)x; Class c = m.getDeclaringClass(); short kind = c.isInterface() ? CONSTANT_INTERFACEMETHOD : CONSTANT_METHOD; short ci = getIndex(c); short nt = getNTIndex(m.getName(), getSig(m.getReturnType(), m.getParameterTypes())); short data[] = { kind, ci, nt }; x = data; } else if (x instanceof ProxyAssembler) { ProxyAssembler asm = (ProxyAssembler)x; short ci = getUtfIndex(asm.className.replace('.', '/')); short data[] = { CONSTANT_CLASS, ci }; x = data; } else if (x instanceof AMember) { AMember m = (AMember) x; short kind = !isMethodSig(m.sig) ? CONSTANT_FIELD : m.asm.isInterface() ? CONSTANT_INTERFACEMETHOD : CONSTANT_METHOD; short ci = getIndex(m.asm); short nt = getNTIndex(m.name, m.sig); short data[] = { kind, ci, nt }; x = data; } else if (x instanceof NameAndType) { NameAndType nt = (NameAndType) x; short data[] = { CONSTANT_NAMEANDTYPE, nt.name, nt.sig }; x = data; } cv.setElementAt(x, i); // update } ByteArrayOutputStream bytes = new ByteArrayOutputStream(400); DataOutputStream ds = new DataOutputStream(bytes); ds.writeInt(JAVA_MAGIC); ds.writeShort(JAVA_MINOR_VERSION); ds.writeShort(JAVA_VERSION); int cvsize = cv.size(); ds.writeShort(cvsize); for (int i = 0; i < cv.size(); i++) { Object x = cv.elementAt(i); if (x == null) { continue; } else if (x instanceof short[]) { short data[] = (short[])x; ds.writeByte(data[0]); for (int j = 1; j < data.length; j++) { ds.writeShort(data[j]); } } else if (x instanceof byte[]) { ds.write((byte[])x); } else if (x instanceof Integer) { ds.writeByte(CONSTANT_INTEGER); ds.writeInt(((Integer)x).intValue()); // (do other primitive literal types?) } else { throw new RuntimeException("unexpected"); } } ds.writeShort(modifiers); ds.writeShort(getIndex(this)); ds.writeShort(getIndex(superClass)); ds.writeShort(interfaces.length); for (int i = 0; i < interfaces.length; i++) { ds.writeShort(getIndex(interfaces[i])); } for (int pass = 0; pass <= 1; pass++) { boolean methods = (pass > 0); ds.writeShort(methods ? nmethods : nfields); for (int i = 0; i < members.size(); i++) { AMember m = (AMember) members.elementAt(i); if (m.name.length() == 0 || isMethodSig(m.sig) != methods) { continue; } ds.writeShort(m.mods); ds.writeShort(getUtfIndex(m.name)); ds.writeShort(getUtfIndex(m.sig)); writeAttrs(ds, m.attr); } } AMember m0 = (AMember) members.elementAt(0); writeAttrs(ds, (Vector) m0.attr); // sanity check if (cvsize != cv.size()) { throw new RuntimeException("cvsize"); } return bytes.toByteArray(); } private byte[] getMethodCode(AMember m, ByteArrayOutputStream code) throws IOException { if (code.size() == 0) { if ((current.mods & (Modifier.NATIVE | Modifier.ABSTRACT)) == 0) { current.mods |= Modifier.ABSTRACT; modifiers |= Modifier.ABSTRACT; } return null; } ByteArrayOutputStream bytes = new ByteArrayOutputStream(code.size()+30); DataOutputStream ds = new DataOutputStream(bytes); int slop = 10; // ?? int max_stack = current.locmax + slop; int max_locals = current.spmax + slop; ds.writeShort(max_stack); ds.writeShort(max_locals); ds.writeInt(code.size()); code.writeTo(ds); ds.writeShort(0); // exception_table.length Vector attrs = new Vector(); for (int i = m.attr.size(); --i >= 0; ) { Attr ma = (Attr) m.attr.elementAt(i); if (ma.name.startsWith("Code.")) { m.attr.removeElementAt(i); ma.name = ma.name.substring("Code.".length()); attrs.addElement(ma); getUtfIndex(ma.name); } } writeAttrs(ds, attrs); return bytes.toByteArray(); } private void writeAttrs(DataOutputStream ds, Vector attrs) throws IOException { ds.writeShort(attrs.size()); for (int i = 0; i < attrs.size(); i++) { Attr a = (Attr) attrs.elementAt(i); ds.writeShort(getUtfIndex(a.name)); if (a.data instanceof byte[]) { byte[] xa = (byte[])a.data; ds.writeInt(xa.length); ds.write(xa); } else { throw new RuntimeException("unexpected"); } } } private static final int JAVA_MAGIC = 0xcafebabe; private static final short JAVA_VERSION = 45, JAVA_MINOR_VERSION = 3; private static final short CONSTANT_UTF8 = 1, CONSTANT_UNICODE = 2, CONSTANT_INTEGER = 3, CONSTANT_FLOAT = 4, CONSTANT_LONG = 5, CONSTANT_DOUBLE = 6, CONSTANT_CLASS = 7, CONSTANT_STRING = 8, CONSTANT_FIELD = 9, CONSTANT_METHOD = 10, CONSTANT_INTERFACEMETHOD = 11, CONSTANT_NAMEANDTYPE = 12; }