Package com.googlecode.aviator.code.asm

Source Code of com.googlecode.aviator.code.asm.ASMCodeGenerator$MethodMetaData

/**
*  Copyright (C) 2010 dennis zhuang (killme2008@gmail.com)
*
*  This library 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 library 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 program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
**/
package com.googlecode.aviator.code.asm;

import static com.googlecode.aviator.asm.Opcodes.*;

import java.io.PrintWriter;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicLong;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.ClassExpression;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.asm.ClassVisitor;
import com.googlecode.aviator.asm.ClassWriter;
import com.googlecode.aviator.asm.Label;
import com.googlecode.aviator.asm.MethodVisitor;
import com.googlecode.aviator.asm.util.CheckClassAdapter;
import com.googlecode.aviator.asm.util.TraceClassVisitor;
import com.googlecode.aviator.code.CodeGenerator;
import com.googlecode.aviator.exception.CompileExpressionErrorException;
import com.googlecode.aviator.lexer.token.NumberToken;
import com.googlecode.aviator.lexer.token.Token;
import com.googlecode.aviator.lexer.token.Variable;
import com.googlecode.aviator.parser.AviatorClassLoader;


/**
* Code generator using asm
*
* @author dennis
*
*/
public class ASMCodeGenerator implements CodeGenerator {
    // Class Writer to generate class
    private final ClassWriter classWriter;
    // Trace visitor
    private ClassVisitor traceClassVisitor;
    // Check visitor
    private ClassVisitor checkClassAdapter;
    // Method visitor
    private MethodVisitor mv;
    // Class name
    private final String className;
    // Class loader to define generated class
    private final AviatorClassLoader classLoader;

    private static final AtomicLong CLASS_COUNTER = new AtomicLong();

    /**
     * Operands count to check stack frames
     */
    private int operandsCount = 0;

    private int maxStacks = 0;
    private int maxLocals = 1;


    private void setMaxStacks(int newMaxStacks) {
        if (newMaxStacks > this.maxStacks) {
            this.maxStacks = newMaxStacks;
        }
    }


    public ASMCodeGenerator(AviatorClassLoader classLoader, boolean trace) {
        this.classLoader = classLoader;
        // Generate inner class name
        className = "Script_" + System.currentTimeMillis() + "_" + CLASS_COUNTER.getAndIncrement();
        // Auto compute frames
        classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        if (trace) {
            traceClassVisitor = new TraceClassVisitor(classWriter, new PrintWriter(System.out));
            checkClassAdapter = new CheckClassAdapter(traceClassVisitor);
        }
        else {
            checkClassAdapter = new CheckClassAdapter(classWriter);
        }
        makeConstructor();
        startVisitMethodCode();
    }


    private void startVisitMethodCode() {
        mv =
                checkClassAdapter.visitMethod(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, "run",
                    "(Ljava/util/Map;)Ljava/lang/Object;",
                    "(Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;)Ljava/lang/Object;", null);
        mv.visitCode();
    }


    private void endVisitCode() {

        if (this.operandsCount > 0) {
            loadEnv();
            mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "getValue",
                "(Ljava/util/Map;)Ljava/lang/Object;");
            mv.visitInsn(ARETURN);
            popOperand();
            popOperand();
        }
        else {
            mv.visitInsn(ACONST_NULL);
            mv.visitInsn(ARETURN);
            pushOperand(0);
            popOperand();
        }
        if (this.operandsCount > 0) {
            throw new CompileExpressionErrorException("operand stack is not empty,count=" + operandsCount);
        }
        mv.visitMaxs(maxStacks, maxLocals);
        mv.visitEnd();

        checkClassAdapter.visitEnd();
    }


    /**
     * Make a default constructor
     */
    private void makeConstructor() {
        checkClassAdapter.visit(AviatorEvaluator.BYTECODE_VER, ACC_PUBLIC + ACC_SUPER, className, null, "java/lang/Object",
            null);

        {
            mv = checkClassAdapter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            mv.visitInsn(RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
    }


    /**
     * Make a label
     *
     * @return
     */
    private Label makeLabel() {
        return new Label();
    }


    /*
     * (non-Javadoc)
     *
     * @see
     * com.googlecode.aviator.code.CodeGenerator#onAdd(com.googlecode.aviator
     * .lexer.token.Token)
     */
    public void onAdd(Token<?> lookhead) {
        doArthOperation("add");
    }


    /**
     * Do arithmetic operation
     *
     * @param methodName
     */
    private void doArthOperation(String methodName) {
        loadEnv();
        mv
            .visitMethodInsn(
                INVOKEVIRTUAL,
                "com/googlecode/aviator/runtime/type/AviatorObject",
                methodName,
                "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");
        popOperand();
        popOperand();
    }


    /**
     * Pop a operand from stack
     */
    private void popOperand() {
        this.operandsCount--;
    }


    /*
     * (non-Javadoc)
     *
     * @see
     * com.googlecode.aviator.code.CodeGenerator#onSub(com.googlecode.aviator
     * .lexer.token.Token)
     */
    public void onSub(Token<?> lookhead) {
        doArthOperation("sub");
    }


    /*
     * (non-Javadoc)
     *
     * @see
     * com.googlecode.aviator.code.CodeGenerator#onMult(com.googlecode.aviator
     * .lexer.token.Token)
     */
    public void onMult(Token<?> lookhead) {
        doArthOperation("mult");
    }


    /*
     * (non-Javadoc)
     *
     * @see
     * com.googlecode.aviator.code.CodeGenerator#onDiv(com.googlecode.aviator
     * .lexer.token.Token)
     */
    public void onDiv(Token<?> lookhead) {
        doArthOperation("div");
    }


    /*
     * (non-Javadoc)
     *
     * @see
     * com.googlecode.aviator.code.CodeGenerator#onMod(com.googlecode.aviator
     * .lexer.token.Token)
     */
    public void onMod(Token<?> lookhead) {
        doArthOperation("mod");
    }


    /**
     * Do logic operation "&&" left operand
     */
    public void onAndLeft(Token<?> lookhead) {
        loadEnv();
        mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "booleanValue",
            "(Ljava/util/Map;)Z");
        Label l0 = new Label();
        l0stack.push(l0);
        mv.visitJumpInsn(IFEQ, l0);

        popOperand(); // boolean object
        popOperand(); // environment

    }


    /**
     * Do logic operation "&&" right operand
     */
    public void onAndRight(Token<?> lookhead) {
        loadEnv();
        mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "booleanValue",
            "(Ljava/util/Map;)Z");
        mv.visitJumpInsn(IFEQ, l0stack.peek());
        // Result is true
        mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "TRUE",
            "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
        Label l1 = new Label();
        mv.visitJumpInsn(GOTO, l1);
        mv.visitLabel(l0stack.pop());
        // Result is false
        mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "FALSE",
            "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
        mv.visitLabel(l1);

        popOperand(); // boolean object
        popOperand(); // environment
        pushOperand(0);
    }

    /**
     * Label stack for ternary operator
     */
    private final Stack<Label> l0stack = new Stack<Label>();
    private final Stack<Label> l1stack = new Stack<Label>();


    public void onTernaryBoolean(Token<?> lookhead) {
        loadEnv();
        mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "booleanValue",
            "(Ljava/util/Map;)Z");
        Label l0 = new Label();
        Label l1 = new Label();
        l0stack.push(l0);
        l1stack.push(l1);
        mv.visitJumpInsn(IFEQ, l0);
        popOperand();
        popOperand();
        pushOperand(1); // add two booleans

        popOperand(); // pop the last result
    }


    public void onTernaryLeft(Token<?> lookhead) {
        mv.visitJumpInsn(GOTO, l1stack.peek());
        mv.visitLabel(l0stack.pop());
        popOperand(); // pop one boolean
    }


    public void onTernaryRight(Token<?> lookhead) {
        mv.visitLabel(l1stack.pop());
        popOperand(); // pop one boolean
    }


    /**
     * Do logic operation "||" right operand
     */
    public void onJoinRight(Token<?> lookhead) {
        loadEnv();
        mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "booleanValue",
            "(Ljava/util/Map;)Z");
        Label l1 = new Label();
        mv.visitJumpInsn(IFNE, l0stack.peek());
        // Result is False
        mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "FALSE",
            "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
        mv.visitJumpInsn(GOTO, l1);
        mv.visitLabel(l0stack.pop());
        // Result is True
        mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "TRUE",
            "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
        mv.visitLabel(l1);
        popOperand();
        popOperand();
        pushOperand(0);

    }


    /**
     * Do logic operation "||" left operand
     */
    public void onJoinLeft(Token<?> lookhead) {
        loadEnv();
        mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "booleanValue",
            "(Ljava/util/Map;)Z");
        Label l0 = new Label();
        l0stack.push(l0);
        mv.visitJumpInsn(IFNE, l0);

        popOperand();
        popOperand();

    }


    public void onEq(Token<?> lookhead) {
        doCompareAndJump(IFNE);
    }


    public void onMatch(Token<?> lookhead) {
        this.mv.visitInsn(SWAP);
        loadEnv();
        mv
            .visitMethodInsn(
                INVOKEVIRTUAL,
                "com/googlecode/aviator/runtime/type/AviatorObject",
                "match",
                "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");

        popOperand();
        popOperand();
        popOperand();
        pushOperand(0);
    }


    public void onNeq(Token<?> lookhead) {
        doCompareAndJump(IFEQ);
    }


    private void doCompareAndJump(int ints) {
        loadEnv();
        mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "compare",
            "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;)I");
        Label l0 = makeLabel();
        Label l1 = makeLabel();
        mv.visitJumpInsn(ints, l0);
        mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "TRUE",
            "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
        mv.visitJumpInsn(GOTO, l1);
        mv.visitLabel(l0);
        mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "FALSE",
            "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
        mv.visitLabel(l1);
        popOperand();
        popOperand();
        popOperand();
        pushOperand(0);
    }


    public void onGe(Token<?> lookhead) {
        doCompareAndJump(IFLT);
    }


    public void onGt(Token<?> lookhead) {
        doCompareAndJump(IFLE);
    }


    public void onLe(Token<?> lookhead) {
        doCompareAndJump(IFGT);

    }


    public void onLt(Token<?> lookhead) {
        doCompareAndJump(IFGE);
    }


    /**
     *
     * @param extras
     *            额外的栈空间大小
     */
    public void pushOperand(int extras) {
        this.operandsCount++;
        this.operandsCount += extras;
        setMaxStacks(this.operandsCount);
    }


    /**
     * Logic operation '!'
     */
    public void onNot(Token<?> lookhead) {
        pushOperand(0);

        mv.visitTypeInsn(CHECKCAST, "com/googlecode/aviator/runtime/type/AviatorObject");
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "not",
            "(Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");

        popOperand();
        popOperand();
        pushOperand(0);
    }


    /*
     * (non-Javadoc)
     *
     * @see
     * com.googlecode.aviator.code.CodeGenerator#onNeg(com.googlecode.aviator
     * .lexer.token.Token, int)
     */
    public void onNeg(Token<?> lookhead) {
        pushOperand(0);

        mv.visitTypeInsn(CHECKCAST, "com/googlecode/aviator/runtime/type/AviatorObject");
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "neg",
            "(Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");
        popOperand();
        popOperand();
        pushOperand(0);
    }


    /*
     * (non-Javadoc)
     *
     * @see com.googlecode.aviator.code.CodeGenerator#getResult()
     */
    public Expression getResult() {
        endVisitCode();
        byte[] bytes = this.classWriter.toByteArray();
        try {
            return new ClassExpression(classLoader.defineClass(className, bytes));
        }
        catch (Exception e) {
            throw new CompileExpressionErrorException("define class error", e);
        }
    }


    /*
     * (non-Javadoc)
     *
     * @see
     * com.googlecode.aviator.code.CodeGenerator#onConstant(com.googlecode.aviator
     * .lexer.token.Token)
     */
    public void onConstant(Token<?> lookhead) {
        if (lookhead == null) {
            return;
        }
        // load token to stack
        switch (lookhead.getType()) {
        case Number:
            // load numbers
            NumberToken numberToken = (NumberToken) lookhead;
            if (numberToken.getNumber() instanceof Double) {
                mv.visitTypeInsn(NEW, "com/googlecode/aviator/runtime/type/AviatorDouble");
                mv.visitInsn(DUP);
                mv.visitLdcInsn(numberToken.getNumber());
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
                mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorDouble", "<init>",
                    "(Ljava/lang/Number;)V");
            }
            else {
                mv.visitTypeInsn(NEW, "com/googlecode/aviator/runtime/type/AviatorLong");
                mv.visitInsn(DUP);
                mv.visitLdcInsn(numberToken.getNumber());
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
                mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorLong", "<init>",
                    "(Ljava/lang/Number;)V");
            }
            pushOperand(2);
            popOperand();
            popOperand();
            break;
        case String:
            // load string
            mv.visitTypeInsn(NEW, "com/googlecode/aviator/runtime/type/AviatorString");
            mv.visitInsn(DUP);
            mv.visitLdcInsn(lookhead.getValue(null));
            mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorString", "<init>",
                "(Ljava/lang/String;)V");
            pushOperand(2);
            popOperand();
            popOperand();
            break;
        case Pattern:
            // load pattern
            mv.visitTypeInsn(NEW, "com/googlecode/aviator/runtime/type/AviatorPattern");
            mv.visitInsn(DUP);
            mv.visitLdcInsn(lookhead.getValue(null));
            mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorPattern", "<init>",
                "(Ljava/lang/String;)V");
            pushOperand(2);
            popOperand();
            popOperand();
            break;
        case Variable:
            // load variable
            Variable variable = (Variable) lookhead;
            if (variable.equals(Variable.TRUE)) {
                mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "TRUE",
                    "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
                pushOperand(0);
            }
            else if (variable.equals(Variable.FALSE)) {
                mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "FALSE",
                    "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;");
                pushOperand(0);
            }
            else if (variable.equals(Variable.NIL)) {
                mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorNil", "NIL",
                    "Lcom/googlecode/aviator/runtime/type/AviatorNil;");
                pushOperand(0);
            }
            else {
                // check if it is a function name
                // if
                // (AviatorEvaluator.FUNC_MAP.keySet().contains(variable.getLexeme()))
                // {
                // throw new CompileExpressionErrorException("index=" +
                // variable.getStartIndex() + ","
                // + variable.getLexeme() +
                // " is a function name,please don't use it as variable");
                // }

                mv.visitTypeInsn(NEW, "com/googlecode/aviator/runtime/type/AviatorJavaType");
                mv.visitInsn(DUP);
                mv.visitLdcInsn(variable.getLexeme());
                mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorJavaType", "<init>",
                    "(Ljava/lang/String;)V");
                pushOperand(2);
                popOperand();
                popOperand();

            }
            break;
        }

    }


    public void onMethodInvoke(Token<?> lookhead) {
        final MethodMetaData methodMetaData = this.methodMetaDataStack.pop();
        mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/method/AviatorMethod", "invoke",
            "(Ljava/util/Map;Ljava/util/List;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");
        popOperand(); // method object
        popOperand(); // env map
        popOperand(); // argument list
        // pop operands
        for (int i = 0; i < methodMetaData.parameterCount; i++) {
            popOperand();
        }
        pushOperand(0);
    }


    public void onMethodParameter(Token<?> lookhead) {
        this.methodMetaDataStack.peek().parameterCount++;
        // add parameter to list
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z");
        // pop boolean
        mv.visitInsn(POP);
        mv.visitVarInsn(ALOAD, this.methodMetaDataStack.peek().parameterListIndex);
    }

    private static class MethodMetaData {
        String methodName;

        int parameterCount;
        int parameterListIndex;


        public MethodMetaData(String methodName, int parameterListIndex) {
            super();
            this.methodName = methodName;
            this.parameterCount = 0;
            this.parameterListIndex = parameterListIndex;
        }

    }

    private final Stack<MethodMetaData> methodMetaDataStack = new Stack<MethodMetaData>();


    public void onElementStart(Token<?> lookhead) {
        onConstant(lookhead);
        loadEnv();
    }


    public void onElementEnd(Token<?> lookhead) {
        mv
            .visitMethodInsn(
                INVOKEVIRTUAL,
                "com/googlecode/aviator/runtime/type/AviatorJavaType",
                "getElement",
                "(Ljava/util/Map;Lcom/googlecode/aviator/runtime/type/AviatorObject;)Lcom/googlecode/aviator/runtime/type/AviatorObject;");

        popOperand();
        popOperand();
        popOperand();
        pushOperand(0);
    }


    public int getLocalIndex() {
        return maxLocals++;
    }


    public void onMethodName(Token<?> lookhead) {
        String methodName = lookhead.getLexeme();
        createAviatorMethodObject(methodName);
        loadEnv();
        final int parameterLocalIndex = createArugmentList();
        methodMetaDataStack.push(new MethodMetaData(methodName, parameterLocalIndex));

        // pushOperand(0);

    }


    private int createArugmentList() {
        // create argument list
        pushOperand(0);
        pushOperand(0);
        mv.visitTypeInsn(NEW, "java/util/ArrayList");
        mv.visitInsn(DUP);
        popOperand();
        mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V");
        // store to local variable
        final int parameterLocalIndex = getLocalIndex();
        mv.visitVarInsn(ASTORE, parameterLocalIndex);
        mv.visitVarInsn(ALOAD, parameterLocalIndex);
        return parameterLocalIndex;
    }


    private void loadEnv() {
        // load env
        pushOperand(0);
        mv.visitVarInsn(ALOAD, 0);
    }


    private void createAviatorMethodObject(String methodName) {
        pushOperand(0);
        pushOperand(0);
        pushOperand(0);
        mv.visitTypeInsn(NEW, "com/googlecode/aviator/runtime/method/AviatorMethod");
        mv.visitInsn(DUP);
        mv.visitLdcInsn(methodName);
        popOperand();
        popOperand();
        mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/method/AviatorMethod", "<init>",
            "(Ljava/lang/String;)V");
    }
}
TOP

Related Classes of com.googlecode.aviator.code.asm.ASMCodeGenerator$MethodMetaData

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.