com.judoscript.jamaica.ASMJavaClassCreator.java Source code

Java tutorial

Introduction

Here is the source code for com.judoscript.jamaica.ASMJavaClassCreator.java

Source

/* Jamaica, The Java Virtual Machine (JVM) Macro Assembly Language
 * Copyright (C) 2004- James Huang,
 * http://www.judoscript.com/jamaica/index.html
 *
 * This is free software; you can embed, modify and redistribute
 * it under the terms of the GNU Lesser General Public License
 * version 2.1 or up as published by the Free Software Foundation,
 * which you should have received a copy along with this software.
 * In case you did not, please download it from the internet at
 * http://www.gnu.org/copyleft/lesser.html
 *
 * 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.
 *
 *************************** CHANGE LOG ***************************
 *
 * Authors: EB  = Eric Bruneton, Eric.Bruneton@rd.francetelecom.com
 *          JH  = James Jianbo Huang, judoscript@hotmail.com
 *
 * 03-31-2004  EB   Inception based on BCELJavaClassCreator.
 * 04-05-2004  JH   First finished version; all test cases tested.
 *
 **********  No tabs. Indent 2 spaces. Follow the style. **********/

package com.judoscript.jamaica;

import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Modifier;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;

import static org.objectweb.asm.Opcodes.*;

public final class ASMJavaClassCreator extends JavaClassCreator {
    private String fileName;
    private int accessFlags;
    private String className;
    private String superClassName;
    private String[] interfaceNames;
    private Map<String, String> fieldTypes = new HashMap<String, String>();
    private int methodAccessFlags;
    private String methodName;
    private String methodRetType;
    private int locals;
    private boolean lastInstIsReturn;
    private Map<String, Integer> varIndices = new HashMap<String, Integer>();
    private Map<String, String> varTypes = new HashMap<String, String>();
    private Map<String, Label> labels = new HashMap<String, Label>();
    private ClassWriter cv;
    private MethodVisitor mv;

    public ASMJavaClassCreator() {
    }

    public void setSourceFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getSourceFileName() {
        return fileName;
    }

    public String getClassName() throws JavaClassCreatorException {
        return className;
    }

    public String getSuperclassName() throws JavaClassCreatorException {
        return superClassName;
    }

    public String[] getInterfaceNames() throws JavaClassCreatorException {
        return interfaceNames;
    }

    public void startClass(int accessFlags, String className, String superClassName, String[] itfNames)
            throws JavaClassCreatorException {
        this.accessFlags = accessFlags;
        this.className = className;
        this.superClassName = superClassName;
        this.interfaceNames = itfNames;

        cv = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        int len = interfaceNames == null ? 0 : interfaceNames.length;
        String[] itfs = new String[len];
        for (int i = 0; i < len; ++i) {
            itfs[i] = interfaceNames[i].replace('.', '/');
        }
        cv.visit(V1_6, accessFlags, toJVMClassName(className), null, toJVMClassName(superClassName),
                toJVMClassNames(itfs));
    }

    public void startInterface(String className, String[] itfNames) throws JavaClassCreatorException {
        startClass(ACC_PUBLIC | ACC_INTERFACE, className, "java.lang.Object", itfNames);
    }

    public byte[] endClass() throws JavaClassCreatorException {
        fieldTypes.clear();
        fileName = null;
        accessFlags = 0;
        className = null;
        superClassName = null;
        interfaceNames = null;

        byte[] classBytes = cv.toByteArray();
        cv.visitEnd();
        cv = null;

        return classBytes;
    }

    public void addField(int accessFlags, String name, String type) throws JavaClassCreatorException {
        FieldVisitor fv = cv.visitField(accessFlags, name, stringToDescriptor(type), null, null);
        fv.visitEnd();
        String isStaticPrefix = Modifier.isStatic(accessFlags) ? "?" : "";
        fieldTypes.put(name, isStaticPrefix + type);
    }

    public void addConstant(int accessFlags, String name, String type, Object value)
            throws JavaClassCreatorException {
        FieldVisitor fv = cv.visitField(accessFlags, name, stringToDescriptor(type), null, value);
        fv.visitEnd();
        fieldTypes.put(name, "?" + type);
    }

    public String getFieldType(String name) throws JavaClassCreatorException {
        String typ = fieldTypes.get(name);
        if (typ != null && typ.startsWith("?"))
            typ = typ.substring(1);
        return typ;
    }

    public boolean isStaticField(String name) throws JavaClassCreatorException {
        String typ = fieldTypes.get(name);
        return typ != null && typ.startsWith("?");
    }

    public void addAbstractMethod(int accessFlags, String name, String[] argTypes, String[] argNames,
            String returnType, String[] exceptionNames) throws JavaClassCreatorException {
        startMethod(accessFlags, name, argTypes, argNames, returnType, exceptionNames);
    }

    public void addDefaultConstructor(int accessFlags) throws JavaClassCreatorException {
        mv = cv.visitMethod(accessFlags, "<init>", "()V", null, null);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, superClassName.replace('.', '/'), "<init>", "()V");
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
        mv = null;
    }

    public void startMethod(int accessFlags, String name, String[] argTypes, String[] argNames, String returnType,
            String[] exceptionNames) throws JavaClassCreatorException {
        methodAccessFlags = accessFlags;
        methodName = name;
        methodRetType = returnType;
        lastInstIsReturn = false;

        locals = 0;
        if (!Modifier.isStatic(accessFlags)) {
            varIndices.put("this", new Integer(locals++));
            varTypes.put("this", className);
        }

        StringBuffer desc = new StringBuffer();
        desc.append('(');
        int len = argTypes == null ? 0 : argTypes.length;
        for (int i = 0; i < len; ++i) {
            Type t = Type.getType(stringToDescriptor(argTypes[i]));
            desc.append(t.getDescriptor());
            varIndices.put(argNames[i], new Integer(locals));
            varTypes.put(argNames[i], argTypes[i]);
            locals += t.getSize();
        }
        desc.append(')');
        desc.append(stringToDescriptor(returnType));

        len = exceptionNames == null ? 0 : exceptionNames.length;
        String[] exceptions = new String[len];
        for (int i = 0; i < len; ++i) {
            exceptions[i] = stringToDescriptor(exceptionNames[i]);
        }

        mv = cv.visitMethod(accessFlags, name, desc.toString(), null, exceptions);
    }

    public void endMethod() throws JavaClassCreatorException {
        if (!Modifier.isAbstract(methodAccessFlags) && methodRetType.equals("void") && !lastInstIsReturn)
            inst(RETURN);

        mv.visitMaxs(0, 0);
        mv.visitEnd();
        mv = null;

        varIndices.clear();
        varTypes.clear();
        labels.clear();
        methodName = null;
        methodRetType = null;
    }

    public int getMethodAccessFlags() {
        return methodAccessFlags;
    }

    public String getMethodName() {
        return methodName;
    }

    public void addLocalVariable(String name, String type, Object init) throws JavaClassCreatorException {
        Type t = Type.getType(stringToDescriptor(type));
        varIndices.put(name, new Integer(locals));
        varTypes.put(name, type);
        locals += t.getSize();
        if (init != null)
            macroSet(name, init);
    }

    public int getLocalVariableIndex(String name) throws JavaClassCreatorException {
        return varIndices.get(name);
    }

    public String getVariableType(String name) throws JavaClassCreatorException {
        return varTypes.get(name);
    }

    public void addCatchClause(String type, String startLabel, String endLabel, String actionLabel)
            throws JavaClassCreatorException {
        // all labels should have been resolved!
        mv.visitTryCatchBlock(getLabel(startLabel), getLabel(endLabel), getLabel(actionLabel),
                type == null ? "java/lang/Throwable" : type.replace('.', '/'));
    }

    public void setLabel(String label) throws JavaClassCreatorException {
        mv.visitLabel(getLabel(label));
    }

    public Label getLabel(String label) throws JavaClassCreatorException {
        if (label == null)
            throw new JavaClassCreatorException("Label cannot be null.");

        Label l = labels.get(label);
        if (l == null) {
            l = new Label();
            labels.put(label, l);
        }
        return l;
    }

    public void inst(int opcode) throws JavaClassCreatorException {
        lastInstIsReturn = opcode == RETURN;
        int base = 0;
        if (opcode >= 26 && opcode <= 45) // xLOADy
            base = 26;
        else if (opcode >= 59 && opcode <= 78) // xSTOREy
            base = 59;

        if (base > 0) {
            mv.visitVarInsn(base - 5 + (opcode - base) / 4, (opcode - base) % 4);
        } else {
            mv.visitInsn(opcode);
        }
    }

    public void instLoadStoreRet(int opcode, String varName) throws JavaClassCreatorException {
        lastInstIsReturn = false;
        mv.visitVarInsn(opcode, getLocalVariableIndex(varName));
    }

    public void instType(int opcode, String type) throws JavaClassCreatorException {
        lastInstIsReturn = false;
        if (opcode == NEWARRAY) {
            mv.visitIntInsn(opcode, getNewArrayType(type));
            return;
        }
        String desc = stringToDescriptor(type);
        if (desc.startsWith("L")) {
            desc = desc.substring(1, desc.length() - 1);
        }
        mv.visitTypeInsn(opcode, desc);
    }

    public void instGetPut(int opcode, String clsName, String fldName, String type)
            throws JavaClassCreatorException {
        lastInstIsReturn = false;
        mv.visitFieldInsn(opcode, clsName.replace('.', '/'), fldName, stringToDescriptor(type));
    }

    public void instLdc(int opcode, Object constant, String type) throws JavaClassCreatorException {
        lastInstIsReturn = false;
        if (type != null) {
            if (type.equals("int")) {
                if (inst_smallInt(constant, true))
                    return;
            } else if (type.equals("long")) {
                long l = 0;
                try {
                    try {
                        l = ((Number) constant).longValue();
                    } catch (ClassCastException cce1) {
                        try {
                            l = ((Character) constant).charValue();
                        } catch (ClassCastException cce2) {
                            l = ((Boolean) constant).booleanValue() ? 1 : 0;
                        }
                    }
                    if (l == 0 || l == 1) {
                        inst(LCONST_0 + (int) l);
                        return;
                    }
                } catch (Exception x) {
                }
            } else if (type.equals("float")) {
                float f = 0;
                try {
                    try {
                        f = ((Number) constant).floatValue();
                    } catch (ClassCastException cce1) {
                        try {
                            f = ((Character) constant).charValue();
                        } catch (ClassCastException cce2) {
                            f = ((Boolean) constant).booleanValue() ? 1 : 0;
                        }
                    }
                    if (f == 0 || f == 1 || f == 2) {
                        inst(FCONST_0 + (int) f);
                        return;
                    }
                } catch (Exception x) {
                }
            } else if (type.equals("double")) {
                double d = 0;
                try {
                    try {
                        d = ((Number) constant).doubleValue();
                    } catch (ClassCastException cce1) {
                        try {
                            d = ((Character) constant).charValue();
                        } catch (ClassCastException cce2) {
                            d = ((Boolean) constant).booleanValue() ? 1 : 0;
                        }
                    }
                    if (d == 0 || d == 1) {
                        inst(DCONST_0 + (int) d);
                        return;
                    }
                } catch (Exception x) {
                }
            }
        }

        if (type == null && inst_smallInt(constant, false))
            return;

        mv.visitLdcInsn(checkConst(constant, type));
    }

    public void inst_bipush(byte value) throws JavaClassCreatorException {
        lastInstIsReturn = false;
        mv.visitIntInsn(BIPUSH, value);
    }

    public void inst_sipush(short value) throws JavaClassCreatorException {
        lastInstIsReturn = false;
        mv.visitIntInsn(SIPUSH, value);
    }

    public void inst_iinc(String varName, int inc) throws JavaClassCreatorException {
        lastInstIsReturn = false;
        mv.visitIincInsn(getLocalVariableIndex(varName), inc);
    }

    public void inst_multianewarray(String type, short dim) throws JavaClassCreatorException {
        lastInstIsReturn = false;
        mv.visitMultiANewArrayInsn(stringToDescriptor(type), dim);
    }

    public void instJump(int opcode, String label) throws JavaClassCreatorException {
        lastInstIsReturn = false;
        if (opcode == 200) // GOTO_W automatically handled by ASM
            opcode = GOTO;
        else if (opcode == 201) // JSR_W automatically handled by ASM
            opcode = JSR;
        mv.visitJumpInsn(opcode, getLabel(label));
    }

    public void instInvoke(int opcode, String clsName, String mthdName, String[] paramTypes, String retType)
            throws JavaClassCreatorException {
        lastInstIsReturn = false;
        if (clsName == null)
            clsName = getClassName();

        StringBuffer desc = new StringBuffer();
        desc.append('(');
        int len = paramTypes == null ? 0 : paramTypes.length;
        for (int i = 0; i < len; ++i)
            desc.append(stringToDescriptor(paramTypes[i]));
        desc.append(')');
        desc.append(stringToDescriptor(retType));
        mv.visitMethodInsn(opcode, clsName.replace('.', '/'), mthdName, desc.toString());
    }

    // tableswitch and lookupswitch names are inverted !!!

    public void inst_tableswitch(int[] consts, String[] labels, String defaultLabel)
            throws JavaClassCreatorException {
        lastInstIsReturn = false;
        int len = labels == null ? 0 : labels.length;
        Label[] lbls = new Label[len];
        for (int i = 0; i < len; ++i)
            lbls[i] = getLabel(labels[i]);
        mv.visitLookupSwitchInsn(getLabel(defaultLabel), consts, lbls);
    }

    public void inst_lookupswitch(int startConst, String[] labels, String defaultLabel)
            throws JavaClassCreatorException {
        lastInstIsReturn = false;
        int len = labels == null ? 0 : labels.length;
        Label[] lbls = new Label[len];
        for (int i = 0; i < len; ++i)
            lbls[i] = getLabel(labels[i]);
        mv.visitTableSwitchInsn(startConst, startConst + labels.length - 1, getLabel(defaultLabel), lbls);
    }

    public static Object checkConst(Object val, String type) {
        if (type == null) {
            if (val instanceof Integer || val instanceof Long || val instanceof Float || val instanceof Double)
                return val;
            if (val instanceof Number)
                return new Integer(((Number) val).intValue());
            if (val instanceof Boolean)
                return new Integer(((Boolean) val).booleanValue() ? 1 : 0);
            if (val instanceof Character)
                return new Integer(((Character) val).charValue());

            return val.toString();
        }

        int tcode = 0;
        if (type.equals("boolean"))
            tcode = 8;
        else if (type.equals("byte"))
            tcode = 7;
        else if (type.equals("char"))
            tcode = 6;
        else if (type.equals("short"))
            tcode = 5;
        else if (type.equals("int"))
            tcode = 4;
        else if (type.equals("long"))
            tcode = 3;
        else if (type.equals("float"))
            tcode = 2;
        else if (type.equals("double"))
            tcode = 1;

        if (tcode == 0) // String
            return val.toString();

        if (val instanceof Number) {
            switch (tcode) {
            case 1: // double
                return new Double(((Number) val).doubleValue());
            case 2: // float
                return new Float(((Number) val).floatValue());
            case 3: // long
                return new Long(((Number) val).longValue());
            default: // int, short, char, byte, boolean
                return new Integer(((Number) val).intValue());
            }
        }

        int i = 0;
        if (val instanceof Character)
            i = ((Character) val).charValue();
        else if (val instanceof Boolean)
            i = ((Boolean) val).booleanValue() ? 1 : 0;
        switch (tcode) {
        case 1: // double
            return new Double(i);
        case 2: // float
            return new Float(i);
        case 3: // long
            return new Long(i);
        default: // int, short, char, byte, boolean
            return new Integer(i);
        }
    }

    public static int getNewArrayType(String type) throws JavaClassCreatorException {
        try {
            if (type.equals("boolean"))
                return T_BOOLEAN;
            if (type.equals("char"))
                return T_CHAR;
            if (type.equals("float"))
                return T_FLOAT;
            if (type.equals("double"))
                return T_DOUBLE;
            if (type.equals("byte"))
                return T_BYTE;
            if (type.equals("short"))
                return T_SHORT;
            if (type.equals("int"))
                return T_INT;
            if (type.equals("long"))
                return T_LONG;
        } catch (Exception e) {
        }
        throw new JavaClassCreatorException("Invalid type for newarray instruction: " + type);
    }

} // end of class ASMJavaClassCreator.