org.zoeey.ztpl.compiler.ByteCodeHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.zoeey.ztpl.compiler.ByteCodeHelper.java

Source

/*
 * 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;
    }
}