Plan to write bytecode by yourself instead of using asm (full version)

original
2014/04/08 09:38
Reading number 637

At present, Beetl2.0 uses ASM to write section codes, but I find that ASM still occupies a large space of about 45K (compressed into jar). If you write by yourself, it only takes 6K (estimated), so I plan to change to handwritten bytecode before the release of Beetl2.0.

ByteCodeWriter implements a property access class. Through javap, the content is as follows

 E:\>javap -c Test.class public class org.beetl.sample.s01.Test extends org.beetl.core.resolver.AttributeAccess {   public org.beetl.sample.s01.Test();     Code:        0: aload_0        1: invokespecial #8                  // Method org/beetl/core/resolver/AttributeAccess. "<init>":()V        4: return   public java.lang.Object value(java.lang.Object, java.lang.Object);     Code:        0: aload_1        1: checkcast     #12                 // class org/beetl/core/User        4: invokevirtual #14                 // Method org/beetl/core/User.getAge:()I        7: invokestatic  #18                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;       10: areturn }


 package org.beetl.core.resolver; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.beetl.core.lab.TestUser; public class FiledAccessByteCodeWriter { static final int MAGIC = 0xCAFEBABE; static final byte CONS_CLASS = 7; static final byte CONS_UTF8 = 1; static final byte CONS_METHODREF = 10; static final byte CONS_NAME_AND_TYPE = 12; static final byte CONS_DOUBLE = 6; static final short ALOAD_0 = 42; static final short ALOAD_1 = 43; static final short ALOAD_2 = 44; static final short INVOKE_SPECIAL = 183; static final short INVOKE_VIRTUAL = 182; static final short RETURN = 177; static final short ARETURN = 176; static final short CHECK_CAST = 192; static final short INVOKE_STATIC = 184; static final String parentCls = "org/beetl/core/resolver/AttributeAccess"; static final String initFunction = "<init>"; static final String initFunctionDesc = "()V"; static final String valueFunction = "value"; static final String valueFunctionDesc = "(Ljava/lang/Object; Ljava/lang/Object;)Ljava/lang/Object; "; static final String code = "Code"; //outbox,inbox Static final String integerClass="org/feed/core/util/NumberUtil";//Optimized unpacking static final String valueOfFunction = "valueOf"; static final String intValueOfFunctionDesc = "(I)Ljava/lang/Integer; "; static final String shortClass = "java/lang/Short"; static final String shortValueOfFunctionDesc = "(S)Ljava/lang/Short; "; static final String booleanClass = "java/lang/Boolean"; static final String booleanValueOfFunctionDesc = "(Z)Ljava/lang/Boolean; "; static final String doubleClass = "java/lang/Double"; static final String doubleValueOfFunctionDesc = "(D)Ljava/lang/Double; "; static String longClass = "java/lang/Long"; static String longValueOfFunctionDesc = "(J)Ljava/lang/Long; "; byte[] cached = new byte[256]; List<Object[]> constPool = new ArrayList<Object[]>(); Map<String, Short> utfMap = new HashMap<String, Short>(); Map<String, Short> classMap = new HashMap<String, Short>(); String cls = "org/beetl/sample/s01/Test1"; String targetCls = "org/beetl/core/lab/TestUser"; String targetFunction = "getAge"; String targetFunctionDesc = "()I"; String retByteCodeType = "I"; static ASMClassLoader loader = new ASMClassLoader(); static class ASMClassLoader extends ClassLoader { public Class defineClass(String name, byte[] b) { return defineClass(name, b, 0, b.length); } } /** *Try to write bytecode code instead of asm.jar to reduce the size of beetl * @param args */ public static void main(String[] args) throws Exception { Class c = TestUser.class; String name = "contacts"; String methodName = "getContacts"; Class returnType = String[].class; FiledAccessByteCodeWriter cw = new FiledAccessByteCodeWriter(c,  name, methodName, returnType); byte[] bs = cw.getClassByte(); //		OutputStream ins = new FileOutputStream("e:/Test1.class"); //		ins.write(bs); //		ins.close(); TestUser user = new TestUser("joelli"); Class fieldAccessorClass = loader.defineClass(c.getName() + "_" + name, bs); AttributeAccess ac = (AttributeAccess) fieldAccessorClass.newInstance(); Object result = ac.value(user, name); System.out.println(result); } public FiledAccessByteCodeWriter(Class c,  String name, String methodName, Class returnType) { String cname = c.getName().replace(".", "/"); this.targetCls = cname; this.cls = cname + "_" + name; this.targetFunction = methodName; String[] returnArray = this.getRetrunTypeDesc(returnType); String returnTypeClass = returnArray[0]; this.retByteCodeType = returnArray[1]; this.targetFunctionDesc = "()" + returnTypeClass; } public byte[] getClassByte() throws Exception { ByteArrayOutputStream bs = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bs); write(out); return bs.toByteArray(); } //	public Object getObject() throws Exception //	{ //		FiledAccessByteCodeWriter cw = new FiledAccessByteCodeWriter(); //		byte[] bs = cw.getClassByte(); //		Class c = loader.defineClass("org.beetl.sample.s01.Test1", bs); //		Object o = c.newInstance(); // //		return o; // //	} public void write(DataOutputStream out) throws Exception { //For the first placeholder out.writeInt(MAGIC); out.writeShort(0); //jdk5 out.writeShort(49); int clsIndex = this.registerClass(this.cls); int parentIndex = this.registerClass(this.parentCls); byte[] initMethod = getInitMethod(); byte[] valueMethod = this.getProxyMethod(); //constpool-size out.writeShort(this.constPool.size() + 1); writeConstPool(out); out.writeShort(33);// public class out.writeShort(clsIndex); out.writeShort(parentIndex); out.writeShort(0); // interface count; out.writeShort(0); // filed count; //Writing method out.writeShort(2); // method count; out.write(initMethod); out.write(valueMethod); out.writeShort(0); // class-attribute-info } public void writeConstPool(DataOutputStream out) throws Exception { for (Object[] array : this.constPool) { int tag = (Byte) array[0]; out.writeByte(tag); switch (tag) { case CONS_CLASS: out.writeShort((Short) array[1]); break; case CONS_UTF8: out.writeShort((Short) array[1]); byte[] content = (byte[]) array[2]; out.write(content); break; case CONS_METHODREF: //class & nameAndType out.writeShort((Short) array[1]); out.writeShort((Short) array[2]); break; case CONS_NAME_AND_TYPE: //name & desc  out.writeShort((Short) array[1]); out.writeShort((Short) array[2]); break; default: throw new RuntimeException("tag=" + tag); } } } public byte[] getProxyMethod() throws Exception { ByteArrayOutputStream bs = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bs); out.writeShort(1); // public  int nameIndex = this.registerUTFString(this.valueFunction); out.writeShort(nameIndex); int descIndex = this.registerUTFString(this.valueFunctionDesc); out.writeShort(descIndex); out.writeShort(1); // attributeCount byte[] initCodeAttr = proxyCodeAttr(); out.write(initCodeAttr); return bs.toByteArray(); } public byte[] proxyCodeAttr() throws Exception { ByteArrayOutputStream bs = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bs); int index = this.registerUTFString("Code"); out.writeShort(index); byte[] codes = proxyCodes(); //Attribute length int attrlen = 4 + 4 + codes.length + 4; out.writeInt(attrlen); if (this.retByteCodeType.equals("D") || this.retByteCodeType.equals("J")) { out.writeShort(2); } else { out.writeShort(1); // stack,default 1,long or double shoud be 2. } out.writeShort(3); // local var out.writeInt(codes.length); out.write(codes); // codes; out.writeShort(0); // exceptions out.writeShort(0); // attr-info return bs.toByteArray(); } public byte[] proxyCodes() throws Exception { ByteArrayOutputStream bs = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bs); out.writeByte(ALOAD_1); out.writeByte(this. CHECK_CAST); short classIndex = this.registerClass(this.targetCls); out.writeShort(classIndex); out.writeByte(INVOKE_VIRTUAL); int methodIndex = registerMethod(this.targetCls,  this.targetFunction, this.targetFunctionDesc); out.writeShort(methodIndex); if (this.retByteCodeType.equals("I")) { out.writeByte(INVOKE_STATIC); methodIndex = registerMethod(this.integerClass,  this.valueOfFunction, this.intValueOfFunctionDesc); out.writeShort(methodIndex); } else if (this.retByteCodeType.equals("S")) { out.writeByte(INVOKE_STATIC); methodIndex = registerMethod(this.shortClass,  this.valueOfFunction, this.shortValueOfFunctionDesc); out.writeShort(methodIndex); } else if (this.retByteCodeType.equals("D")) { out.writeByte(INVOKE_STATIC); methodIndex = registerMethod(this.doubleClass,  this.valueOfFunction, this.doubleValueOfFunctionDesc); out.writeShort(methodIndex); } else if (this.retByteCodeType.equals("J")) { out.writeByte(INVOKE_STATIC); methodIndex = registerMethod(this.longClass,  this.valueOfFunction, this.longValueOfFunctionDesc); out.writeShort(methodIndex); } out.writeByte(ARETURN - 256); return bs.toByteArray(); } public byte[] getInitMethod() throws Exception { ByteArrayOutputStream bs = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bs); out.writeShort(1); // public  int nameIndex = this.registerUTFString("<init>"); out.writeShort(nameIndex); int descIndex = this.registerUTFString("()V"); out.writeShort(descIndex); out.writeShort(1); // attributeCount byte[] initCodeAttr = initCodeAttr(); out.write(initCodeAttr); return bs.toByteArray(); } public byte[] initCodeAttr() throws Exception { ByteArrayOutputStream bs = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bs); int index = this.registerUTFString("Code"); out.writeShort(index); byte[] codes = initCodes(); //Attribute length int attrlen = 4 + 4 + codes.length + 4; out.writeInt(attrlen); out.writeShort(1); // stack out.writeShort(1); // local var out.writeInt(codes.length); out.write(codes); // codes; out.writeShort(0); // exceptions out.writeShort(0); // attr-info return bs.toByteArray(); } public byte[] initCodes() throws Exception { ByteArrayOutputStream bs = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bs); out.writeByte(ALOAD_0); out.writeByte(INVOKE_SPECIAL); int index = registerMethod(this.parentCls, "<init>", "()V"); out.writeShort(index); out.writeByte(RETURN - 256); return bs.toByteArray(); } public short registerClass(String clsName) { Short index = classMap.get(clsName); if (index !=  null) return index; short clsNameIndex = registerUTFString(clsName); Object[] array = new Object[] { this.CONS_CLASS, clsNameIndex }; this.constPool.add(array); index = getCurrentIndex(); classMap.put(clsName, index); return index; } public short registerMethod(String clsName,  String method, String desc) { short clsNameIndex = this.registerClass(clsName); short nameAndTypeIndex = registerNameAndType(method, desc); Object[] array = new Object[] { this.CONS_METHODREF,  clsNameIndex, nameAndTypeIndex }; this.constPool.add(array); return getCurrentIndex(); } public short registerNameAndType(String method, String desc) { short nameIndex = registerUTFString(method); short descIndex = registerUTFString(desc); Object[] array = new Object[] { this.CONS_NAME_AND_TYPE,  nameIndex, descIndex }; this.constPool.add(array); return getCurrentIndex(); } public short registerUTFString(String str) { Short index = this.utfMap.get(str); if (index !=  null) return index; try { byte[] bs = str.getBytes("UTF-8"); short len = (short) bs.length; Object[] array = new Object[] { CONS_UTF8, len, bs }; this.constPool.add(array); index = getCurrentIndex(); this.utfMap.put(str, index); return index; } catch (UnsupportedEncodingException e) { //not happen throw new RuntimeException(e); } } public short getCurrentIndex() { return (short) (this.constPool.size()); } private String[] getRetrunTypeDesc(Class c) { StringBuilder sb = new StringBuilder(); String returnType = ""; if (c.isArray()) { sb.append(c.getName().replace(".", "/")); } else if (c == int.class) { sb.append("I"); returnType = "I"; } else if (c == boolean.class) { sb.append("Z"); returnType = "Z"; } else if (c == char.class) { sb.append("C"); returnType = "C"; } else if (c == short.class) { sb.append("S"); returnType = "S"; } else if (c == float.class) { sb.append("F"); returnType = "F"; } else if (c == long.class) { sb.append("J"); returnType = "J"; } else if (c == double.class) { sb.append("D"); returnType = "D"; } else { sb.append("L").append(c.getName().replace(".", "/")).append(";"); returnType = "L"; } return new String[] { sb.toString(), returnType }; } }



Expand to read the full text
Loading
Click to join the discussion 🔥 (2) Post and join the discussion 🔥
Reward
two comment
three Collection
zero fabulous
 Back to top
Top