Java tutorial
/* * MoXie (SysTem128@GMail.Com) 2009-5-2 19:55:34 * * Copyright © 2008-2009 Zoeey.Org * Code license: GNU Lesser General Public License Version 3 * http://www.gnu.org/licenses/lgpl-3.0.txt */ package org.zoeey.ztpl.compiler; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.zoeey.common.ZObject; import org.zoeey.ztpl.FunctionAble; import org.zoeey.ztpl.ZtplConstant; /** * ? * @author MoXie(SysTem128@GMail.Com) */ public class ByteCodeHelper { /** * Writer ??? */ public static final int VAR_INDEX_WRITER = 1; /** * ParamsMap ??? */ public static final int VAR_INDEX_PARAMSMAP = 2; /** * Ztpl ??? */ public static final int VAR_INDEX_ZTPL = 3; /** * */ private ClassWriter cw = null; /** * */ private MethodVisitor mv = null; /** * */ private CompileTracker tracker = null; /** * ? * @param mv */ public ByteCodeHelper() { } /** * template */ public void newClass(String className) { cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); /** * */ cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, // ZtplConstant.CLASS_URI + className, null, "java/lang/Object", // new String[] { ZtplConstant.TEMPLATE_INTERFACE }); /** * construct */ { mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); // mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } /** * TemplateAble#publish */ { mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "publish", // "(Ljava/io/Writer;Ljava/util/Map;Lorg/zoeey/ztpl/Ztpl;)V", // null, new String[] { "java/io/IOException" }); mv.visitCode(); tracker = new CompileTracker(); } } /** * template */ public void endClass() { mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(tracker.next(), tracker.next()); mv.visitEnd(); cw.visitEnd(); } /** * template */ public void callClass(String className) { className = ZtplConstant.CLASS_URI + className; mv.visitTypeInsn(Opcodes.NEW, className); mv.visitInsn(Opcodes.DUP); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, className, "<init>", "()V"); // mv.visitVarInsn(Opcodes.ALOAD, VAR_INDEX_WRITER); mv.visitVarInsn(Opcodes.ALOAD, VAR_INDEX_PARAMSMAP); mv.visitVarInsn(Opcodes.ALOAD, VAR_INDEX_ZTPL); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ZtplConstant.TEMPLATE_INTERFACE// , "publish", "(Ljava/io/Writer;Ljava/util/Map;Lorg/zoeey/ztpl/Ztpl;)V"); } /** * ?? * @return */ public byte[] getCode() { return cw.toByteArray(); } /** * ??<br /> * @param obj ?? * @return ?? */ public int newVar(Object obj) { int pos = tracker.next(); visitObject(obj); mv.visitVarInsn(Opcodes.ASTORE, pos); return pos; } /** * ??? * @param pos ParamMap?? */ public void getVar(int pos) { mv.visitVarInsn(Opcodes.ALOAD, pos); } /** * ???? * @param key */ public void getContext(String key) { mv.visitVarInsn(Opcodes.ALOAD, VAR_INDEX_PARAMSMAP); mv.visitLdcInsn(key); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); } /** * * @param className ?? * @param paramsPos ? * @param pos */ public void callFunction(String className, int paramsPos) { String classPath = className.replace('.', '/'); /** * ? */ mv.visitTypeInsn(Opcodes.NEW, classPath); mv.visitInsn(Opcodes.DUP); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classPath, "<init>", "()V"); // invoke call() mv.visitVarInsn(Opcodes.ALOAD, paramsPos); mv.visitVarInsn(Opcodes.ALOAD, VAR_INDEX_ZTPL); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, FunctionAble.class.getName().replace('.', '/'), "call", // "(Lorg/zoeey/ztpl/ParamsMap;Lorg/zoeey/ztpl/Ztpl;)Ljava/lang/String;"); } /** * Writer * @param str ? */ public void writeStr(String str) { mv.visitVarInsn(Opcodes.ALOAD, VAR_INDEX_WRITER); mv.visitLdcInsn(str); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/Writer"// , "append", "(Ljava/lang/CharSequence;)Ljava/io/Writer;"); mv.visitInsn(Opcodes.POP); } /** * Writer?? * @param key ???? */ public void writeVar(String key) { mv.visitVarInsn(Opcodes.ALOAD, VAR_INDEX_WRITER); getContext(key); mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/CharSequence"); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/Writer"// , "append", "(Ljava/lang/CharSequence;)Ljava/io/Writer;"); mv.visitInsn(Opcodes.POP); } /** * WriterContext?? * @param pos ??? */ public void writeVar(int pos) { mv.visitVarInsn(Opcodes.ALOAD, VAR_INDEX_WRITER); mv.visitVarInsn(Opcodes.ALOAD, pos); mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/CharSequence"); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/Writer"// , "append", "(Ljava/lang/CharSequence;)Ljava/io/Writer;"); mv.visitInsn(Opcodes.POP); } /** * ???nullWriterContext?? * @param key */ public void writeVarJugeNull(String key) { // if ($key != null) getContext(key); Label l0 = new Label(); mv.visitJumpInsn(Opcodes.IFNULL, l0); writeVar(key); // end if mv.visitLabel(l0); } /** * ?<br /> * ? var paramsMap_* = new ParamsMap();<br /> * ex. {echo varA="a" varB="b"} <br /> * ?? map{"varA":"a","varB":"b"} * */ public int newMap() { int pos = tracker.next(); mv.visitTypeInsn(Opcodes.NEW, "org/zoeey/ztpl/ParamsMap"); mv.visitInsn(Opcodes.DUP); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "org/zoeey/ztpl/ParamsMap", "<init>", "()V"); mv.visitVarInsn(Opcodes.ASTORE, pos); return pos; } /** * ???ParamsMap * ?paramsMap.put(key,value); * @param mapPos ParamsMap? * @param key * @param valuePos ?? */ public void putToMap(int mapPos, String key, int valuePos) { mv.visitVarInsn(Opcodes.ALOAD, mapPos); mv.visitLdcInsn(key); mv.visitVarInsn(Opcodes.ALOAD, valuePos); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); mv.visitInsn(Opcodes.POP); } /** * ???ParamsMap * ?paramsMap.get(key); * @param mapPos ParamsMap? * @param key * @param valuePos ?? * @return pos ? */ public int getFromMap(int mapPos, String key) { int pos = tracker.next(); mv.visitVarInsn(Opcodes.ALOAD, mapPos); mv.visitLdcInsn(key); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); mv.visitVarInsn(Opcodes.ASTORE, pos); return pos; } /** * * @param obj * @return */ public void visitObject(Object obj) { if (obj == null) { mv.visitInsn(Opcodes.ACONST_NULL); } else { @SuppressWarnings("unchecked") Class<Object> clazz = (Class<Object>) obj.getClass(); if (int.class.isAssignableFrom(clazz) || Integer.class.isAssignableFrom(clazz)) { visitInt(new ZObject(obj).toInteger()); } else if (double.class.isAssignableFrom(clazz) || Double.class.isAssignableFrom(clazz)) { visitDouble(new ZObject(obj).toDouble()); } else if (Boolean.class.isAssignableFrom(clazz)) { visitBoolean(new ZObject(obj).toBoolean()); } else { mv.visitLdcInsn(obj); } } } /** * * @param bool * @return */ private ByteCodeHelper visitBoolean(boolean bool) { if (bool) { mv.visitInsn(Opcodes.ICONST_1); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;"); } else { mv.visitInsn(Opcodes.ICONST_0); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;"); } return this; } private ByteCodeHelper visitDouble(double db) { mv.visitLdcInsn(db); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;"); return this; } /** * * @param num * @return */ private ByteCodeHelper visitInt(int num) { /** * * @param num */ int label = 0; switch (num) { case -1: mv.visitInsn(Opcodes.ICONST_M1); break; case 0: mv.visitInsn(Opcodes.ICONST_0); break; case 1: mv.visitInsn(Opcodes.ICONST_1); break; case 2: mv.visitInsn(Opcodes.ICONST_2); break; case 3: mv.visitInsn(Opcodes.ICONST_3); break; case 4: mv.visitInsn(Opcodes.ICONST_4); break; case 5: mv.visitInsn(Opcodes.ICONST_5); break; default: if (num <= Byte.MAX_VALUE && num >= Byte.MIN_VALUE) { mv.visitIntInsn(Opcodes.BIPUSH, num); } else if (num <= Short.MAX_VALUE && num >= Short.MIN_VALUE) { mv.visitIntInsn(Opcodes.SIPUSH, num); } else if (num <= Integer.MAX_VALUE && num >= Integer.MIN_VALUE) { mv.visitLdcInsn(num); /** * ?? */ } else if (num <= Long.MAX_VALUE && num >= Long.MIN_VALUE) { mv.visitLdcInsn(num); label = 1; } else { // todo throw something } } if (label == 1) { mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;"); } else { mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); } return this; } /** * ? * @return */ public CompileTracker getTracker() { return tracker; } /** * ?Method * @return */ public MethodVisitor getMethodVisitor() { return mv; } }