com.codename1.tools.translator.Parser.java Source code

Java tutorial

Introduction

Here is the source code for com.codename1.tools.translator.Parser.java

Source

/*
 * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Codename One designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *  
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 * 
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * Please contact Codename One through http://www.codenameone.com/ if you 
 * need additional information or have any questions.
 */

package com.codename1.tools.translator;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.TypePath;
import org.objectweb.asm.commons.JSRInlinerAdapter;

/**
 *
 * @author Shai Almog
 */
public class Parser extends ClassVisitor {
    private ByteCodeClass cls;
    private String clsName;
    private static String[] nativeSources;
    private static List<ByteCodeClass> classes = new ArrayList<ByteCodeClass>();

    public static void parse(File sourceFile) throws Exception {
        if (ByteCodeTranslator.verbose) {
            System.out.println("Parsing: " + sourceFile.getAbsolutePath());
        }
        ClassReader r = new ClassReader(new FileInputStream(sourceFile));
        /*if(ByteCodeTranslator.verbose) {
        System.out.println("Class: " + r.getClassName() + " derives from: " + r.getSuperName() + " interfaces: " + Arrays.asList(r.getInterfaces()));
        }*/
        Parser p = new Parser();
        p.clsName = r.getClassName().replace('/', '_').replace('$', '_');
        if (p.clsName.startsWith("java_lang_annotation") || p.clsName.startsWith("java_lang_Deprecated")
                || p.clsName.startsWith("java_lang_Override")
                || p.clsName.startsWith("java_lang_SuppressWarnings")) {
            return;
        }
        p.cls = new ByteCodeClass(p.clsName);
        r.accept(p, ClassReader.EXPAND_FRAMES);

        classes.add(p.cls);
    }

    private static ByteCodeClass getClassByName(String name) {
        name = name.replace('/', '_').replace('$', '_');
        for (ByteCodeClass bc : classes) {
            if (bc.getClsName().equals(name)) {
                return bc;
            }
        }
        return null;
    }

    private static void appendClassOffset(ByteCodeClass bc, List<Integer> clsIds) {
        if (bc.getBaseClassObject() != null) {
            if (!clsIds.contains(bc.getBaseClassObject().getClassOffset())) {
                clsIds.add(bc.getBaseClassObject().getClassOffset());
                appendClassOffset(bc.getBaseClassObject(), clsIds);
            }
        }
        if (bc.getBaseInterfacesObject() != null) {
            for (ByteCodeClass c : bc.getBaseInterfacesObject()) {
                if (c != null && !clsIds.contains(c.getClassOffset())) {
                    clsIds.add(c.getClassOffset());
                    if (c.getBaseClassObject() != null) {
                        appendClassOffset(c, clsIds);
                    }
                }
            }
        }
    }

    private static ArrayList<String> constantPool = new ArrayList<String>();

    public static ByteCodeClass getClassObject(String name) {
        for (ByteCodeClass cls : classes) {
            if (cls.getClsName().equals(name)) {
                return cls;
            }
        }
        return null;
    }

    /**
     * Adds the given string to the hardcoded constant pool strings returns the offset in the pool
     */
    public static int addToConstantPool(String s) {
        int i = constantPool.indexOf(s);
        if (i < 0) {
            constantPool.add(s);
            return constantPool.size() - 1;
        }
        return i;
    }

    private static void generateClassAndMethodIndexHeader(File outputDirectory) throws Exception {
        int classOffset = 0;
        int methodOffset = 0;
        ArrayList<BytecodeMethod> methods = new ArrayList<BytecodeMethod>();
        for (ByteCodeClass bc : classes) {
            bc.setClassOffset(classOffset);
            classOffset++;

            methodOffset = bc.updateMethodOffsets(methodOffset);
            methods.addAll(bc.getMethods());
        }

        StringBuilder bld = new StringBuilder();
        StringBuilder bldM = new StringBuilder();
        bldM.append("#include \"cn1_class_method_index.h\"\n");
        bldM.append("#include \"cn1_globals.h\"\n\n");
        bld.append("#ifndef __CN1_CLASS_METHOD_INDEX_H__\n#define __CN1_CLASS_METHOD_INDEX_H__\n\n");

        bld.append("// maps to offsets in the constant pool below\nextern int classNameLookup[];\n");
        bldM.append("// maps to offsets in the constant pool below\nint classNameLookup[] = {");
        boolean first = true;
        for (ByteCodeClass bc : classes) {
            if (first) {
                bldM.append("\n    ");
            } else {
                bldM.append(",\n    ");
            }
            first = false;
            bldM.append(addToConstantPool(bc.getClsName().replace('_', '.')));
            bldM.append("");
        }
        bldM.append("};\n\n");

        for (ByteCodeClass bc : classes) {
            bld.append("#define cn1_class_id_");
            bld.append(bc.getClsName());
            bld.append(" ");
            bld.append(bc.getClassOffset());
            bld.append("\n");
        }

        int arrayId = classes.size() + 1;

        bld.append("#define cn1_array_start_offset ");
        bld.append(arrayId);
        bld.append("\n");

        // leave space for primitive arrays
        arrayId += 100;

        for (ByteCodeClass bc : classes) {
            bld.append("#define cn1_array_1_id_");
            bld.append(bc.getClsName());
            bld.append(" ");
            bld.append(arrayId);
            bld.append("\n");
            arrayId++;

            bld.append("#define cn1_array_2_id_");
            bld.append(bc.getClsName());
            bld.append(" ");
            bld.append(arrayId);
            bld.append("\n");
            arrayId++;

            bld.append("#define cn1_array_3_id_");
            bld.append(bc.getClsName());
            bld.append(" ");
            bld.append(arrayId);
            bld.append("\n");
            arrayId++;

            /*bld.append("#define cn1_array_4_id_");
            bld.append(bc.getClsName());
            bld.append(" ");
            bld.append(arrayId);
            bld.append("\n");
            arrayId++;*/
        }

        bld.append("\n\n");

        bld.append("// maps to offsets in the constant pool below\nextern int methodNameLookup[];\n");
        bldM.append("// maps to offsets in the constant pool below\nint methodNameLookup[] = {");
        first = true;
        for (BytecodeMethod m : methods) {
            if (first) {
                bldM.append("\n    ");
            } else {
                bldM.append(",\n    ");
            }
            first = false;
            bldM.append(addToConstantPool(m.getMethodName()));
            bldM.append("");
        }
        bldM.append("};\n\n");

        ArrayList<Integer> instances = new ArrayList<Integer>();
        int counter = 0;
        for (ByteCodeClass bc : classes) {
            /*bld.append("extern int classInstanceOfArr");
            bld.append(counter);
            bld.append("[];\n");*/
            bldM.append("int classInstanceOfArr");
            bldM.append(counter);
            bldM.append("[] = {");
            counter++;
            appendClassOffset(bc, instances);

            for (Integer i : instances) {
                bldM.append(i);
                bldM.append(", ");
            }
            instances.clear();
            bldM.append("-1};\n");
        }
        bld.append("extern int *classInstanceOf[];\n");
        bldM.append("int *classInstanceOf[");
        bldM.append(classes.size());
        bldM.append("] = {");
        first = true;
        counter = 0;
        for (ByteCodeClass bc : classes) {
            if (first) {
                bldM.append("\n    ");
            } else {
                bldM.append(",\n    ");
            }
            first = false;
            bldM.append("classInstanceOfArr");
            bldM.append(counter);
            counter++;
        }
        bldM.append("};\n\n");

        bld.append("#define CN1_CONSTANT_POOL_SIZE ");
        bld.append(constantPool.size());
        bld.append("\n\nextern const char * const constantPool[];\n");

        bldM.append("\n\nconst char * const constantPool[] = {\n");
        first = true;
        int offset = 0;
        for (String con : constantPool) {
            if (first) {
                bldM.append("\n    \"");
            } else {
                bldM.append(",\n    \"");
            }
            first = false;
            try {
                bldM.append(encodeString(con));
            } catch (Throwable t) {
                t.printStackTrace();
                System.out.println("Error writing the constant pool string: '" + con + "'");
                System.exit(1);
            }
            bldM.append("\" /* ");
            bldM.append(offset);
            offset++;
            bldM.append(" */");
        }
        bldM.append("};\n\nint classListSize = ");
        bldM.append(classes.size());
        bldM.append(";\n");

        for (ByteCodeClass bc : classes) {
            bldM.append("extern struct clazz class__");
            bldM.append(bc.getClsName().replace('/', '_').replace('$', '_'));
            bldM.append(";\n");
        }
        bldM.append("\n\nstruct clazz* classesList[] = {");
        first = true;
        for (ByteCodeClass bc : classes) {
            if (first) {
                bldM.append("\n    ");
            } else {
                bldM.append(",\n    ");
            }
            first = false;
            bldM.append("    &class__");
            bldM.append(bc.getClsName().replace('/', '_').replace('$', '_'));
        }
        bldM.append("};\n\n\n");

        // generate the markStatics method
        for (ByteCodeClass bc : classes) {
            bc.appendStaticFieldsExtern(bldM);
        }
        bldM.append(
                "\n\nextern int recursionKey;\nvoid markStatics(CODENAME_ONE_THREAD_STATE) {\n    recursionKey++;\n");
        for (ByteCodeClass bc : classes) {
            bc.appendStaticFieldsMark(bldM);
        }
        bldM.append("}\n\n");

        bld.append("\n\n#endif // __CN1_CLASS_METHOD_INDEX_H__\n");

        FileOutputStream fos = new FileOutputStream(new File(outputDirectory, "cn1_class_method_index.h"));
        fos.write(bld.toString().getBytes("UTF-8"));
        fos.close();
        fos = new FileOutputStream(new File(outputDirectory, "cn1_class_method_index.m"));
        fos.write(bldM.toString().getBytes("UTF-8"));
        fos.close();
    }

    private static String encodeString(String con) {
        String str = con.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r")
                .replace("\t", "\\t");
        return encodeStringSlashU(str);
    }

    private static String encodeStringSlashU(String str) {
        int len = str.length();
        char[] chr = str.toCharArray();
        for (int iter = 0; iter < len; iter++) {
            char c = chr[iter];
            if (c > 127) {
                // needs encoding... Verify there are no more characters to encode
                StringBuilder d = new StringBuilder();
                for (int internal = 0; internal < len; internal++) {
                    c = chr[internal];
                    if (c > 127) {
                        d.append("~~u");
                        d.append(fourChars(Integer.toHexString(c)));
                    } else {
                        d.append(c);
                    }
                }
                return d.toString();
            }
        }
        return str;
    }

    private static String fourChars(String s) {
        switch (s.length()) {
        case 1:
            return "000" + s;
        case 2:
            return "00" + s;
        case 3:
            return "0" + s;
        }
        return s;
    }

    public static void writeOutput(File outputDirectory) throws Exception {
        System.out.println("outputDirectory is: " + outputDirectory.getAbsolutePath());
        if (ByteCodeClass.getMainClass() == null) {
            System.out.println(
                    "Error main class is not defined. The main class name is expected to have a public static void main(String[]) method and it is assumed to reside in the com.package.name directory");
            System.exit(1);
        }
        String file = "Unknown File";
        try {
            for (ByteCodeClass bc : classes) {
                // special case for object
                if (bc.getClsName().equals("java_lang_Object")) {
                    continue;
                }
                file = bc.getClsName();
                bc.setBaseClassObject(getClassByName(bc.getBaseClass()));
                List<ByteCodeClass> lst = new ArrayList<ByteCodeClass>();
                for (String s : bc.getBaseInterfaces()) {
                    ByteCodeClass bcode = getClassByName(s);
                    if (bcode == null) {
                        System.out.println("Error while working with the class: " + s + " file:" + file
                                + " no class definition");
                    } else {
                        lst.add(getClassByName(s));
                    }
                }
                bc.setBaseInterfacesObject(lst);
            }
            boolean foundNewUnitTests = true;
            while (foundNewUnitTests) {
                foundNewUnitTests = false;
                for (ByteCodeClass bc : classes) {
                    if (!bc.isUnitTest() && bc.getBaseClassObject() != null
                            && bc.getBaseClassObject().isUnitTest()) {
                        bc.setIsUnitTest(true);
                        foundNewUnitTests = true;
                    }
                }
            }

            for (ByteCodeClass bc : classes) {
                file = bc.getClsName();
                bc.updateAllDependencies();
            }
            ByteCodeClass.markDependencies(classes);
            classes = ByteCodeClass.clearUnmarked(classes);

            // load the native sources (including user native code) 
            readNativeFiles(outputDirectory);

            // loop over methods and start eliminating the body of unused methods
            eliminateUnusedMethods();

            generateClassAndMethodIndexHeader(outputDirectory);

            boolean concatenate = "true".equals(System.getProperty("concatenateFiles", "false"));
            ConcatenatingFileOutputStream cos = concatenate ? new ConcatenatingFileOutputStream(outputDirectory)
                    : null;

            for (ByteCodeClass bc : classes) {
                file = bc.getClsName();
                writeFile(bc, outputDirectory, cos);
            }
            if (cos != null)
                cos.realClose();

        } catch (Throwable t) {
            System.out.println("Error while working with the class: " + file);
            t.printStackTrace();
            if (t instanceof Exception) {
                throw (Exception) t;
            }
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            }
        }
    }

    private static void readNativeFiles(File outputDirectory) throws IOException {
        File[] mFiles = outputDirectory.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return file.getName().endsWith(".m");
            }
        });
        nativeSources = new String[mFiles.length];
        for (int iter = 0; iter < mFiles.length; iter++) {
            DataInputStream di = new DataInputStream(new FileInputStream(mFiles[iter]));
            byte[] dat = new byte[(int) mFiles[iter].length()];
            di.readFully(dat);
            nativeSources[iter] = new String(dat, "UTF-8");
        }

    }

    private static void eliminateUnusedMethods() {
        usedByNativeCheck();
        eliminateUnusedMethods(0);
    }

    private static void eliminateUnusedMethods(int depth) {
        boolean found = false;
        found = cullMethods(found);
        cullClasses(found, depth);
    }

    private static boolean cullMethods(boolean found) {
        for (ByteCodeClass bc : classes) {
            bc.unmark();
            if (bc.isIsInterface() || bc.getBaseClass() == null) {
                continue;
            }
            for (BytecodeMethod mtd : bc.getMethods()) {
                if (mtd.isEliminated() || mtd.isUsedByNative() || mtd.isMain()
                        || mtd.getMethodName().equals("__CLINIT__") || mtd.getMethodName().equals("finalize")
                        || mtd.isNative()) {
                    continue;
                }

                if (!isMethodUsed(mtd)) {
                    if (isMethodUsedByBaseClassOrInterface(mtd, bc)) {
                        continue;
                    }
                    found = true;
                    mtd.setEliminated(true);
                    /*if(ByteCodeTranslator.verbose) {
                    System.out.println("Eliminating method: " + mtd.getClsName() + "." + mtd.getMethodName());
                    }*/
                }
            }
        }
        return found;
    }

    private static boolean isMethodUsedByBaseClassOrInterface(BytecodeMethod mtd, ByteCodeClass cls) {
        boolean b = checkMethodUsedByBaseClassOrInterface(mtd, cls.getBaseClassObject());
        if (b) {
            return true;
        }
        for (ByteCodeClass bc : cls.getBaseInterfacesObject()) {
            b = checkMethodUsedByBaseClassOrInterface(mtd, bc);
            if (b) {
                return true;
            }
        }
        return false;
    }

    private static boolean checkMethodUsedByBaseClassOrInterface(BytecodeMethod mtd, ByteCodeClass cls) {
        if (cls == null) {
            return false;
        }
        if (cls.getBaseInterfacesObject() != null) {
            for (ByteCodeClass bc : cls.getBaseInterfacesObject()) {
                for (BytecodeMethod m : bc.getMethods()) {
                    if (m.getMethodName().equals(mtd.getMethodName())) {
                        if (m.isUsedByNative()) {
                            return true;
                        }
                        break;
                    }
                }
            }
        }
        for (BytecodeMethod m : cls.getMethods()) {
            if (m.getMethodName().equals(mtd.getMethodName())) {
                if (m.isUsedByNative()) {
                    return true;
                }
                break;
            }
        }
        return false;
    }

    private static void cullClasses(boolean found, int depth) {
        if (found && depth < 3) {
            for (ByteCodeClass bc : classes) {
                bc.updateAllDependencies();
            }
            //int classCount = classes.size();
            ByteCodeClass.markDependencies(classes);
            List<ByteCodeClass> tmp = ByteCodeClass.clearUnmarked(classes);
            /*if(ByteCodeTranslator.verbose) {
            System.out.println("Classes removed from: " + classCount + " to " + classes.size());
            for(ByteCodeClass bc : classes) {
            if(!tmp.contains(bc)) {
            System.out.println("Removed class: " + bc.getClsName());
            }
            }
            }*/
            classes = tmp;
            eliminateUnusedMethods(depth + 1);
        }
    }

    private static void usedByNativeCheck() {
        for (ByteCodeClass bc : classes) {
            //java_lang_Thread_runImpl___long
            for (BytecodeMethod mtd : bc.getMethods()) {
                // check native code        
                StringBuilder b = new StringBuilder();
                mtd.appendFunctionPointer(b);
                String str = b.toString();

                for (String s : nativeSources) {
                    if (s.contains(str)) {
                        mtd.setUsedByNative(true);
                        break;
                    }
                }
            }
        }
    }

    private static boolean isMethodUsed(BytecodeMethod m) {
        for (ByteCodeClass bc : classes) {
            for (BytecodeMethod mtd : bc.getMethods()) {
                if (mtd.isEliminated() || mtd == m) {
                    continue;
                }
                if (mtd.isMethodUsed(m)) {
                    return true;
                }
            }
        }

        // check native code        
        StringBuilder b = new StringBuilder();
        m.appendFunctionPointer(b);
        String str = b.toString();
        for (String s : nativeSources) {
            if (s.contains(str)) {
                return true;
            }
        }

        return false;
    }

    private static void writeFile(ByteCodeClass cls, File outputDir,
            ConcatenatingFileOutputStream writeBufferInstead) throws Exception {
        OutputStream outMain = writeBufferInstead != null
                && ByteCodeTranslator.output == ByteCodeTranslator.OutputType.OUTPUT_TYPE_IOS ? writeBufferInstead
                        : new FileOutputStream(new File(outputDir,
                                cls.getClsName() + "." + ByteCodeTranslator.output.extension()));

        if (outMain instanceof ConcatenatingFileOutputStream) {
            ((ConcatenatingFileOutputStream) outMain).beginNextFile(cls.getClsName());
        }
        if (ByteCodeTranslator.output == ByteCodeTranslator.OutputType.OUTPUT_TYPE_IOS) {
            outMain.write(cls.generateCCode(classes).getBytes());
            outMain.close();

            // we also need to write the header file for iOS
            String headerName = cls.getClsName() + ".h";
            FileOutputStream outHeader = new FileOutputStream(new File(outputDir, headerName));
            outHeader.write(cls.generateCHeader().getBytes());
            outHeader.close();
        } else {
            outMain.write(cls.generateCSharpCode().getBytes());
            outMain.close();
        }
    }

    public Parser() {
        super(Opcodes.ASM5);
    }

    @Override
    public void visitEnd() {
        super.visitEnd();
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        BytecodeMethod mtd = new BytecodeMethod(clsName, access, name, desc, signature, exceptions);
        cls.addMethod(mtd);
        JSRInlinerAdapter a = new JSRInlinerAdapter(
                new MethodVisitorWrapper(super.visitMethod(access, name, desc, signature, exceptions), mtd), access,
                name, desc, signature, exceptions);
        return a;
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        ByteCodeField fld = new ByteCodeField(clsName, access, name, desc, signature, value);
        cls.addField(fld);
        return new FieldVisitorWrapper(super.visitField(access, name, desc, signature, value));
    }

    @Override
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
        super.visitInnerClass(name, outerName, innerName, access);
    }

    @Override
    public void visitAttribute(Attribute attr) {
        super.visitAttribute(attr);
    }

    @Override
    public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
        return new AnnotationVisitorWrapper(super.visitTypeAnnotation(typeRef, typePath, desc, visible));
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        return new AnnotationVisitorWrapper(super.visitAnnotation(desc, visible));
    }

    @Override
    public void visitOuterClass(String owner, String name, String desc) {
        super.visitOuterClass(owner, name, desc);
    }

    @Override
    public void visitSource(String source, String debug) {
        cls.setSourceFile(source);
        super.visitSource(source, debug);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        cls.setBaseClass(superName);
        cls.setBaseInterfaces(interfaces);
        if ((access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT) {
            cls.setIsAbstract(true);
        }
        if ((access & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE) {
            cls.setIsInterface(true);
        }
        if ((access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL) {
            cls.setFinalClass(true);
        }
        if ("com/codename1/testing/UnitTest".equals(superName)
                || "com/codename1/testing/AbstractTest".equals(superName)) {
            cls.setIsUnitTest(true);
        }
        if ((access & Opcodes.ACC_ENUM) == Opcodes.ACC_ENUM) {
            cls.setIsEnum(true);
        }
        super.visit(version, access, name, signature, superName, interfaces);
    }

    class MethodVisitorWrapper extends MethodVisitor {
        private BytecodeMethod mtd;

        public MethodVisitorWrapper(MethodVisitor mv, BytecodeMethod mtd) {
            super(Opcodes.ASM5, mv);
            this.mtd = mtd;
        }

        @Override
        public void visitEnd() {
            super.visitEnd();
        }

        @Override
        public void visitMaxs(int maxStack, int maxLocals) {
            mtd.setMaxes(maxStack, maxLocals);
            super.visitMaxs(maxStack, maxLocals);
        }

        @Override
        public void visitLineNumber(int line, Label start) {
            mtd.addDebugInfo(line);
            super.visitLineNumber(line, start);
        }

        @Override
        public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start,
                Label[] end, int[] index, String desc, boolean visible) {
            return new AnnotationVisitorWrapper(
                    super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, desc, visible));
        }

        @Override
        public void visitLocalVariable(String name, String desc, String signature, Label start, Label end,
                int index) {
            mtd.addLocalVariable(name, desc, signature, start, end, index);
            super.visitLocalVariable(name, desc, signature, start, end, index);
        }

        @Override
        public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc,
                boolean visible) {
            return new AnnotationVisitorWrapper(super.visitTryCatchAnnotation(typeRef, typePath, desc, visible));
        }

        @Override
        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
            mtd.addTryCatchBlock(start, end, handler, type);
            super.visitTryCatchBlock(start, end, handler, type);
        }

        @Override
        public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            return new AnnotationVisitorWrapper(super.visitInsnAnnotation(typeRef, typePath, desc, visible));
        }

        @Override
        public void visitMultiANewArrayInsn(String desc, int dims) {
            mtd.addMultiArray(desc, dims);
            super.visitMultiANewArrayInsn(desc, dims);
        }

        @Override
        public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
            mtd.addSwitch(dflt, keys, labels);
            super.visitLookupSwitchInsn(dflt, keys, labels);
        }

        @Override
        public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
            int[] keys = new int[labels.length];
            int counter = min;
            for (int iter = 0; iter < keys.length; iter++) {
                keys[iter] = counter;
                counter++;
            }
            mtd.addSwitch(dflt, keys, labels);
            super.visitTableSwitchInsn(min, max, dflt, labels);
        }

        @Override
        public void visitIincInsn(int var, int increment) {
            mtd.addIInc(var, increment);
            super.visitIincInsn(var, increment);
        }

        @Override
        public void visitLdcInsn(Object cst) {
            mtd.addLdc(cst);
            super.visitLdcInsn(cst);
        }

        @Override
        public void visitLabel(Label label) {
            mtd.addLabel(label);
            super.visitLabel(label);
        }

        @Override
        public void visitJumpInsn(int opcode, Label label) {
            mtd.addJump(opcode, label);
            super.visitJumpInsn(opcode, label);
        }

        @Override
        public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
            super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
            mtd.addInvoke(opcode, owner, name, desc, itf);
            super.visitMethodInsn(opcode, owner, name, desc, itf);
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
            super.visitMethodInsn(opcode, owner, name, desc);
        }

        @Override
        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
            mtd.addField(opcode, owner, name, desc);
            super.visitFieldInsn(opcode, owner, name, desc);
        }

        @Override
        public void visitTypeInsn(int opcode, String type) {
            mtd.addTypeInstruction(opcode, type);
            super.visitTypeInsn(opcode, type);
        }

        @Override
        public void visitVarInsn(int opcode, int var) {
            mtd.addVariableOperation(opcode, var);
            super.visitVarInsn(opcode, var);
        }

        @Override
        public void visitIntInsn(int opcode, int operand) {
            mtd.addVariableOperation(opcode, operand);
            super.visitIntInsn(opcode, operand);
        }

        @Override
        public void visitInsn(int opcode) {
            mtd.addInstruction(opcode);
            super.visitInsn(opcode);
        }

        @Override
        public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
            super.visitFrame(type, nLocal, local, nStack, stack);
        }

        @Override
        public void visitCode() {
            super.visitCode();
        }

        @Override
        public void visitAttribute(Attribute attr) {
            super.visitAttribute(attr);
        }

        @Override
        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
            if (mv == null)
                return null;
            return new AnnotationVisitorWrapper(super.visitParameterAnnotation(parameter, desc, visible));
        }

        @Override
        public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            if (mv == null)
                return null;
            return new AnnotationVisitorWrapper(super.visitTypeAnnotation(typeRef, typePath, desc, visible));
        }

        @Override
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            if (mv == null)
                return null;
            return new AnnotationVisitorWrapper(super.visitAnnotation(desc, visible));
        }

        @Override
        public AnnotationVisitor visitAnnotationDefault() {
            if (mv == null)
                return null;
            return new AnnotationVisitorWrapper(super.visitAnnotationDefault());
        }

        @Override
        public void visitParameter(String name, int access) {
            super.visitParameter(name, access);
        }

    }

    class FieldVisitorWrapper extends FieldVisitor {

        public FieldVisitorWrapper(FieldVisitor fv) {
            super(Opcodes.ASM5, fv);
        }

        @Override
        public void visitEnd() {
            super.visitEnd();
        }

        @Override
        public void visitAttribute(Attribute attr) {
            super.visitAttribute(attr);
        }

        @Override
        public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            return super.visitTypeAnnotation(typeRef, typePath, desc, visible);
        }

        @Override
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            return super.visitAnnotation(desc, visible);
        }

    }

    class AnnotationVisitorWrapper extends AnnotationVisitor {

        public AnnotationVisitorWrapper(AnnotationVisitor av) {
            super(Opcodes.ASM5, av);
        }

        @Override
        public void visitEnd() {
            super.visitEnd();
        }

        @Override
        public AnnotationVisitor visitArray(String name) {
            if (av == null)
                return null;
            return super.visitArray(name);
        }

        @Override
        public AnnotationVisitor visitAnnotation(String name, String desc) {
            if (av == null)
                return null;
            return super.visitAnnotation(name, desc);
        }

        @Override
        public void visitEnum(String name, String desc, String value) {
            super.visitEnum(name, desc, value);
        }

        @Override
        public void visit(String name, Object value) {
            super.visit(name, value);
        }

    }
}