org.simantics.databoard.tests.testASM.java Source code

Java tutorial

Introduction

Here is the source code for org.simantics.databoard.tests.testASM.java

Source

/*******************************************************************************
 *  Copyright (c) 2010 Association for Decentralized Information Management in
 *  Industry THTH ry.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Contributors:
 *      VTT Technical Research Centre of Finland - initial API and implementation
 *******************************************************************************/
package org.simantics.databoard.tests;

import static org.objectweb.asm.Opcodes.AALOAD;
import static org.objectweb.asm.Opcodes.AASTORE;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ARRAYLENGTH;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IF_ICMPLT;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INSTANCEOF;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.ISTORE;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.PUTSTATIC;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_6;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.simantics.databoard.Methods;
import org.simantics.databoard.binding.error.BindingConstructionException;
import org.simantics.databoard.method.InvokeException;
import org.simantics.databoard.method.MethodInterface;
import org.simantics.databoard.method.MethodInterface.AsyncResult;
import org.simantics.databoard.method.MethodInterface.ExecutionError;
import org.simantics.databoard.method.MethodInterface.Method;
import org.simantics.databoard.method.MethodInterfaceUtil;
import org.simantics.databoard.method.MethodNotSupportedException;
import org.simantics.databoard.method.MethodTypeBinding;
import org.simantics.databoard.method.RuntimeInvokeException;

public class testASM {

    public interface Abu {

        Integer x(Integer i) throws testASM.MyException, testASM.RandomException, InvokeException;

        double y();

        String hello(int[] args) throws testASM.MyException, InvokeException;

    }

    public interface Rajapinta {

        void m0();

        int m1();

        int m2(int arg0);

        int m3(int arg0, int arg1);

        Integer m10(int color, String name) throws InvokeException, ExecutionError;

        int m11(int color, String name) throws InvokeException, ExecutionError;
    }

    static class Toteutus implements Rajapinta {
        Method _10, _11;

        public Integer m10(int color, String name) throws InvokeException, ExecutionError {
            AsyncResult result = _10.invoke(new Object[] { color, name });
            try {
                return (Integer) result.waitForResponse();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        public int m11(int color, String name) throws InvokeException, ExecutionError {
            AsyncResult result = _11.invoke(new Object[] { color, name });
            try {
                return (Integer) result.waitForResponse();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void m0() {
            // TODO Auto-generated method stub

        }

        @Override
        public int m1() {
            // TODO Auto-generated method stub
            return 666;
        }

        @Override
        public int m2(int arg0) {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public int m3(int arg0, int arg1) {
            // TODO Auto-generated method stub
            return 0;
        }

    }

    public static byte[] dump() throws Exception {
        ClassWriter cw = new ClassWriter(0);
        FieldVisitor fv;
        MethodVisitor mv;
        AnnotationVisitor av0;

        cw.visit(V1_6, ACC_SUPER, "org/simantics/data/Virhe", null, "java/lang/Object", null);

        cw.visitSource("testASM.java", null);

        {
            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(157, l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            mv.visitInsn(RETURN);
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitLocalVariable("this", "Lorg/simantics/data/Virhe;", null, l0, l1, 0);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        cw.visitEnd();

        return cw.toByteArray();
    }

    public static class MyException extends Exception {
        String msg;

        public MyException(String msg) {
            this.msg = msg;
        }
    }

    public static class RandomException extends Exception {
        String msg;

        public RandomException(String msg) {
            this.msg = msg;
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println(Object[][].class.getName());

        System.out.println(Abu.class.getName());
        System.out.println(void.class.getName());
        System.out.println(int.class.getName());

        try {
            Toteutus t = new Toteutus();

            Virhe3 server = new Virhe3();
            MethodInterface serverMi = MethodInterfaceUtil.bindInterface(Abu.class, server);

            StubClassLoader cl = new StubClassLoader();
            Class<?> virhe = cl.loadClass("org.simantics.data.testASM$Abu_Stub");
            //         Class<?> virhe = cl.loadClass("org.simantics.data.Virhe2");

            Constructor<?> c = virhe.getConstructor(MethodInterface.class);
            Abu client = (Abu) c.newInstance(serverMi);
            //         client = new Virhe2(serverMi);

            System.out.println(client);
            System.out.println(client.hello(new int[] { 0, 5, 0 }));
            System.out.println(client.y());
            System.out.println(client.x(5));
        } catch (testASM.MyException e) {
            System.err.printf("MyException = %s", e.msg);
        }

    }

}

class Virhe3 implements testASM.Abu {
    @Override
    public Integer x(Integer i) throws testASM.MyException, testASM.RandomException, InvokeException {
        //         throw new testASM.MyException("XYZ");
        throw new testASM.RandomException("XYZ");
        //      return 7;
    }

    @Override
    public double y() {
        return 5;
    }

    @Override
    public String hello(int[] args) throws testASM.MyException, InvokeException {
        return "World " + Arrays.toString(args);
    }

}

class Virhe2 implements testASM.Abu {
    //   static final MethodBinding x = new MethodBinding("x", DataTypes.getBinding(int.class), DataTypes.getBinding(void.class), DataTypes.getErrorBinding());
    static final Class<?> interfaze = testASM.Abu.class;
    static final MethodTypeBinding[] bindings;

    static {
        java.lang.reflect.Method[] methods = interfaze.getMethods();
        Arrays.sort(methods, MethodInterfaceUtil.methodComparator);

        bindings = new MethodTypeBinding[methods.length];
        for (int i = 0; i < bindings.length; i++) {
            try {
                bindings[i] = Methods.getMethodTypeBinding(methods[i]);
            } catch (BindingConstructionException e) {
                throw new RuntimeException(e);
            }
        }
    }

    MethodInterface mi;
    Method[] methods;

    public Virhe2(MethodInterface mi) throws MethodNotSupportedException {
        this.mi = mi;
        methods = new Method[bindings.length];
        for (int i = 0; i < bindings.length; i++) {
            methods[i] = mi.getMethod(bindings[i]);
        }
    }

    public Integer x(Integer arg1) throws InvokeException, testASM.MyException {
        Method m = methods[1];

        Object args[] = new Object[] { arg1 };

        AsyncResult result = m.invoke(args);
        try {
            return (Integer) result.waitForResponse();
        } catch (ExecutionError e) {
            Object cause = e.getError();
            if (cause instanceof testASM.MyException)
                throw (testASM.MyException) cause;
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public double y() {
        Method m = methods[2];

        Object args[] = new Object[] {};

        AsyncResult result = m.invoke(args);
        try {
            return (Double) result.waitForResponse();
        } catch (InvokeException e) {
            throw new RuntimeInvokeException(e);
        } catch (ExecutionError e) {
            Object cause = e.getError();
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String hello(int[] args) throws testASM.MyException, InvokeException {
        return null;
    }
}

class StubClassLoader extends ClassLoader {

    public StubClassLoader() {
        super();
    }

    public StubClassLoader(ClassLoader parent) {
        super(parent);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        if (name.endsWith("_Stub")) {
            Class<?> interfaze = loadClass(name.substring(0, name.length() - 5));
            ClassWriter cw = new ClassWriter(0);
            createImpl(name, interfaze, cw);
            byte[] b = cw.toByteArray();
            Class<?> clazz = defineClass(name, b, 0, b.length);
            return clazz;
        }
        return super.findClass(name);
    }

    static String classToTypeDescriptor(Class<?> clazz) {
        if (clazz == void.class)
            return "V";
        if (clazz == boolean.class)
            return "Z";
        if (clazz == char.class)
            return "C";
        if (clazz == byte.class)
            return "B";
        if (clazz == short.class)
            return "S";
        if (clazz == int.class)
            return "I";
        if (clazz == float.class)
            return "F";
        if (clazz == long.class)
            return "J";
        if (clazz == double.class)
            return "D";
        if (clazz == void.class)
            return "V";
        if (clazz.isArray())
            return clazz.getName();
        return "L" + clazz.getName().replaceAll("\\.", "/") + ";";
    }

    void createImpl(String className, Class<?> interfaze, ClassWriter cw) {
        FieldVisitor fv;
        MethodVisitor mv;
        AnnotationVisitor av0;

        java.lang.reflect.Method[] methods = interfaze.getMethods();
        Arrays.sort(methods, MethodInterfaceUtil.methodComparator);

        String classResourceName = className.replaceAll("\\.", "/");
        String classTypeDescriptor = "L" + classResourceName + ";";
        String interfaceResourceName = classResourceName.substring(0, classResourceName.length() - 5);
        String interfaceTypeDescriptor = "L" + interfaceResourceName + ";";

        cw.visit(V1_6, ACC_SUPER | ACC_PUBLIC, classResourceName, null, "java/lang/Object",
                new String[] { interfaceResourceName });

        // Imports
        Set<Class<?>> imports = new HashSet<Class<?>>();
        imports.add(AsyncResult.class);
        imports.add(ExecutionError.class);
        imports.add(Method.class);
        imports.add(interfaze);

        for (java.lang.reflect.Method m : methods) {
            for (Class<?> clazz : m.getExceptionTypes()) {
                imports.add(clazz);
            }
            for (Class<?> clazz : m.getParameterTypes()) {
                imports.add(clazz);
            }
            imports.add(m.getReturnType());
        }

        for (Class<?> clazz : imports) {
            if (clazz.isPrimitive())
                continue;
            String name = clazz.getName();
            if (name.startsWith("java.lang"))
                continue;
            String resourceName = name.replaceAll("\\.", "/");
            String outerName = resourceName.contains("$") ? resourceName.substring(0, resourceName.indexOf('$'))
                    : null;
            String className_ = clazz.isArray()
                    ? clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - 2)
                    : clazz.getSimpleName();
            int access = ACC_PUBLIC + ACC_STATIC + (clazz.isInterface() ? ACC_INTERFACE + ACC_ABSTRACT : 0);
            //         System.out.printf("name=%s, outerName=%s, innerName=%s\n", resourceName, outerName, className_);
            cw.visitInnerClass(resourceName, outerName, className_, access);
        }

        // Fields
        {
            fv = cw.visitField(ACC_FINAL + ACC_STATIC, "interfaze", "Ljava/lang/Class;", "Ljava/lang/Class<*>;",
                    null);
            fv.visitEnd();
        }
        {
            fv = cw.visitField(ACC_FINAL + ACC_STATIC, "bindings",
                    "[Lorg/simantics/databoard/method/MethodBinding;", null, null);
            fv.visitEnd();
        }
        {
            fv = cw.visitField(0, "mi", "Lorg/simantics/data/session/MethodInterface;", null, null);
            fv.visitEnd();
        }
        {
            fv = cw.visitField(0, "methods", "[Lorg/simantics/data/session/MethodInterface$Method;", null, null);
            fv.visitEnd();
        }

        // Init class - static {}
        {
            mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLdcInsn(Type.getType(interfaceTypeDescriptor));
            mv.visitFieldInsn(PUTSTATIC, classResourceName, "interfaze", "Ljava/lang/Class;");
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitFieldInsn(GETSTATIC, classResourceName, "interfaze", "Ljava/lang/Class;");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;");
            mv.visitVarInsn(ASTORE, 0);
            Label l2 = new Label();
            mv.visitLabel(l2);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETSTATIC, "org/simantics/data/network/MethodInterfaceUtil", "methodComparator",
                    "Ljava/util/Comparator;");
            mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "sort",
                    "([Ljava/lang/Object;Ljava/util/Comparator;)V");
            Label l3 = new Label();
            mv.visitLabel(l3);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitInsn(ARRAYLENGTH);
            mv.visitTypeInsn(ANEWARRAY, "org/simantics/databoard/method/MethodBinding");
            mv.visitFieldInsn(PUTSTATIC, classResourceName, "bindings",
                    "[Lorg/simantics/databoard/method/MethodBinding;");
            Label l4 = new Label();
            mv.visitLabel(l4);
            mv.visitInsn(ICONST_0);
            mv.visitVarInsn(ISTORE, 1);
            Label l5 = new Label();
            mv.visitLabel(l5);
            Label l6 = new Label();
            mv.visitJumpInsn(GOTO, l6);
            Label l7 = new Label();
            mv.visitLabel(l7);
            mv.visitFrame(Opcodes.F_APPEND, 2, new Object[] { "[Ljava/lang/reflect/Method;", Opcodes.INTEGER }, 0,
                    null);
            mv.visitFieldInsn(GETSTATIC, classResourceName, "bindings",
                    "[Lorg/simantics/databoard/method/MethodBinding;");
            mv.visitVarInsn(ILOAD, 1);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ILOAD, 1);
            mv.visitInsn(AALOAD);
            mv.visitMethodInsn(INVOKESTATIC, "org/simantics/databoard/DataTypes", "getMethodBinding",
                    "(Ljava/lang/reflect/Method;)Lorg/simantics/databoard/method/MethodBinding;");
            mv.visitInsn(AASTORE);
            Label l8 = new Label();
            mv.visitLabel(l8);
            mv.visitIincInsn(1, 1);
            mv.visitLabel(l6);
            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
            mv.visitVarInsn(ILOAD, 1);
            mv.visitFieldInsn(GETSTATIC, classResourceName, "bindings",
                    "[Lorg/simantics/databoard/method/MethodBinding;");
            mv.visitInsn(ARRAYLENGTH);
            mv.visitJumpInsn(IF_ICMPLT, l7);
            Label l9 = new Label();
            mv.visitLabel(l9);
            mv.visitInsn(RETURN);
            Label l10 = new Label();
            mv.visitLabel(l10);
            mv.visitLocalVariable("methods", "[Ljava/lang/reflect/Method;", null, l2, l10, 0);
            mv.visitLocalVariable("i", "I", null, l5, l9, 1);
            mv.visitMaxs(4, 2);
            mv.visitEnd();
        }

        // Constructor
        {
            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Lorg/simantics/data/session/MethodInterface;)V", null,
                    new String[] { "org/simantics/data/error/MethodNotSupportedException" });
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitFieldInsn(PUTFIELD, classResourceName, "mi", "Lorg/simantics/data/session/MethodInterface;");
            Label l2 = new Label();
            mv.visitLabel(l2);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETSTATIC, classResourceName, "bindings",
                    "[Lorg/simantics/databoard/method/MethodBinding;");
            mv.visitInsn(ARRAYLENGTH);
            mv.visitTypeInsn(ANEWARRAY, "org/simantics/data/session/MethodInterface$Method");
            mv.visitFieldInsn(PUTFIELD, classResourceName, "methods",
                    "[Lorg/simantics/data/session/MethodInterface$Method;");
            Label l3 = new Label();
            mv.visitLabel(l3);
            mv.visitInsn(ICONST_0);
            mv.visitVarInsn(ISTORE, 2);
            Label l4 = new Label();
            mv.visitLabel(l4);
            Label l5 = new Label();
            mv.visitJumpInsn(GOTO, l5);
            Label l6 = new Label();
            mv.visitLabel(l6);
            mv.visitFrame(Opcodes.F_FULL, 3, new Object[] { classResourceName,
                    "org/simantics/data/session/MethodInterface", Opcodes.INTEGER }, 0, new Object[] {});
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, classResourceName, "methods",
                    "[Lorg/simantics/data/session/MethodInterface$Method;");
            mv.visitVarInsn(ILOAD, 2);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitFieldInsn(GETSTATIC, classResourceName, "bindings",
                    "[Lorg/simantics/databoard/method/MethodBinding;");
            mv.visitVarInsn(ILOAD, 2);
            mv.visitInsn(AALOAD);
            mv.visitMethodInsn(INVOKEINTERFACE, "org/simantics/data/session/MethodInterface", "getMethod",
                    "(Lorg/simantics/databoard/method/MethodBinding;)Lorg/simantics/data/session/MethodInterface$Method;");
            mv.visitInsn(AASTORE);
            Label l7 = new Label();
            mv.visitLabel(l7);
            mv.visitIincInsn(2, 1);
            mv.visitLabel(l5);
            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
            mv.visitVarInsn(ILOAD, 2);
            mv.visitFieldInsn(GETSTATIC, classResourceName, "bindings",
                    "[Lorg/simantics/databoard/method/MethodBinding;");
            mv.visitInsn(ARRAYLENGTH);
            mv.visitJumpInsn(IF_ICMPLT, l6);
            Label l8 = new Label();
            mv.visitLabel(l8);
            mv.visitInsn(RETURN);
            Label l9 = new Label();
            mv.visitLabel(l9);
            mv.visitLocalVariable("this", classTypeDescriptor, null, l0, l9, 0);
            mv.visitLocalVariable("mi", "Lorg/simantics/data/session/MethodInterface;", null, l0, l9, 1);
            mv.visitLocalVariable("i", "I", null, l4, l8, 2);
            mv.visitMaxs(5, 3);
            mv.visitEnd();
        }

        // Method
        int methodNumber = 0;
        for (java.lang.reflect.Method m : methods) {
            String typeDescription = "";
            Class<?>[] params = m.getParameterTypes();
            for (int i = 0; i < params.length; i++) {
                if (i > 0)
                    typeDescription += ";";
                typeDescription += classToTypeDescriptor(params[i]);
            }
            typeDescription = "(" + typeDescription + ")" + classToTypeDescriptor(m.getReturnType());
            System.out.println(typeDescription + " " + m.getName());

            Class<?>[] exceptions = m.getExceptionTypes();
            String[] exceptionResourceNames = new String[exceptions.length];
            for (int i = 0; i < exceptionResourceNames.length; i++) {
                exceptionResourceNames[i] = exceptions[i].getName().replaceAll("\\.", "/");
            }

            // Registers
            // 0 - this
            // 1..argRegs args
            // argRegs+1 - method
            // argRegs+2 - args (the array)
            // argRegs+3 - AsyncResult
            // argRegs+4 - e
            // argRegs+5 - ExecutionError.cause
            // argRegs+6 - e

            int argRegs = 0;
            for (int i = 0; i < params.length; i++) {
                Class<?> clazz = params[i];
                argRegs++;
                if (clazz == long.class || clazz == float.class || clazz == Double.class)
                    argRegs++;
            }

            mv = cw.visitMethod(ACC_PUBLIC, m.getName(), typeDescription, null, exceptionResourceNames);
            mv.visitCode();
            Label l0 = new Label();
            Label l1 = new Label();
            Label l2 = new Label();
            mv.visitTryCatchBlock(l0, l1, l2, "org/simantics/data/session/MethodInterface$ExecutionError");
            Label l3 = new Label();
            mv.visitTryCatchBlock(l0, l1, l3, "java/lang/InterruptedException");
            Label l4 = new Label();
            mv.visitLabel(l4);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, classResourceName, "methods",
                    "[Lorg/simantics/data/session/MethodInterface$Method;");
            // Method m and puts into register 
            mv.visitLdcInsn(Integer.valueOf(methodNumber));
            mv.visitInsn(AALOAD);
            mv.visitVarInsn(ASTORE, argRegs + 1);
            Label l5 = new Label();
            mv.visitLabel(l5);
            mv.visitLdcInsn(Integer.valueOf(params.length));
            mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");

            int register = 1; // argument register   
            for (int i = 0; i < params.length; i++) {
                Class<?> clazz = params[i];
                mv.visitInsn(DUP);
                mv.visitLdcInsn(Integer.valueOf(i));

                if (clazz == byte.class) {
                    mv.visitVarInsn(ILOAD, register++);
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
                } else if (clazz == char.class) {
                    mv.visitVarInsn(ILOAD, register++);
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
                } else if (clazz == boolean.class) {
                    mv.visitVarInsn(ILOAD, register++);
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
                } else if (clazz == byte.class) {
                    mv.visitVarInsn(ILOAD, register++);
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
                } else if (clazz == int.class) {
                    mv.visitVarInsn(ILOAD, register++);
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
                } else if (clazz == long.class) {
                    mv.visitVarInsn(LLOAD, register);
                    register += 2;
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(F)Ljava/lang/Long;");
                } else if (clazz == float.class) {
                    mv.visitVarInsn(FLOAD, register);
                    register += 2;
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
                } else if (clazz == Double.class) {
                    mv.visitVarInsn(DLOAD, register);
                    register += 2;
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
                } else {
                    // Push argument to stack 
                    mv.visitVarInsn(ALOAD, register++);
                }
                mv.visitInsn(AASTORE);
            }

            // Store args to argRegs+2
            mv.visitVarInsn(ASTORE, argRegs + 2);
            Label l6 = new Label();
            mv.visitLabel(l6);
            mv.visitVarInsn(ALOAD, argRegs + 1 /* m */);
            mv.visitVarInsn(ALOAD, argRegs + 2 /*args*/);
            mv.visitMethodInsn(INVOKEINTERFACE, "org/simantics/data/session/MethodInterface$Method", "invoke",
                    "(Ljava/lang/Object;)Lorg/simantics/data/session/MethodInterface$AsyncResult;");
            mv.visitVarInsn(ASTORE, argRegs + 3);
            mv.visitLabel(l0);
            mv.visitVarInsn(ALOAD, argRegs + 3);
            mv.visitMethodInsn(INVOKEINTERFACE, "org/simantics/data/session/MethodInterface$AsyncResult",
                    "waitForResponse", "()Ljava/lang/Object;");
            // TODO Return typecase result
            Class<?> returnType = m.getReturnType();

            if (returnType == void.class) {
                mv.visitInsn(POP);
                mv.visitInsn(RETURN);
            } else if (returnType == int.class) {
                mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
                mv.visitLabel(l1);
                mv.visitInsn(IRETURN);
            } else if (returnType == byte.class) {
                mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "B()");
                mv.visitLabel(l1);
                mv.visitInsn(IRETURN);
            } else if (returnType == char.class) {
                mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
                mv.visitLabel(l1);
                mv.visitInsn(IRETURN);
            } else if (returnType == boolean.class) {
                mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
                mv.visitLabel(l1);
                mv.visitInsn(IRETURN);
            } else if (returnType == short.class) {
                mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
                mv.visitLabel(l1);
                mv.visitInsn(IRETURN);
            } else if (returnType == long.class) {
                mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
                mv.visitLabel(l1);
                mv.visitInsn(LRETURN);
            } else if (returnType == double.class) {
                mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
                mv.visitLabel(l1);
                mv.visitInsn(DRETURN);
            } else if (returnType == float.class) {
                mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
                mv.visitLabel(l1);
                mv.visitInsn(FRETURN);
            } else {
                // Object
                mv.visitTypeInsn(CHECKCAST, returnType.getName().replaceAll("\\.", "/"));
                mv.visitLabel(l1);
                mv.visitInsn(ARETURN);
            }

            // Handle exceptions
            mv.visitLabel(l2);
            mv.visitFrame(Opcodes.F_FULL, argRegs + 4,
                    new Object[] { classResourceName, "java/lang/Integer",
                            "org/simantics/data/session/MethodInterface$Method", "[Ljava/lang/Object;",
                            "org/simantics/data/session/MethodInterface$AsyncResult" },
                    1, new Object[] { "org/simantics/data/session/MethodInterface$ExecutionError" });
            mv.visitVarInsn(ASTORE, argRegs + 4);
            Label l7 = new Label();
            mv.visitLabel(l7);
            mv.visitVarInsn(ALOAD, argRegs + 4);
            mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/data/session/MethodInterface$ExecutionError",
                    "getError", "()Ljava/lang/Object;");
            mv.visitVarInsn(ASTORE, argRegs + 5); // argRegs+5 <- ExecutionError.cause

            Class<?>[] exceptionClasses = m.getExceptionTypes();
            Label nextException[] = new Label[exceptionClasses.length];
            for (int i = 0; i < exceptionClasses.length; i++) {
                Class<?> exceptionClass = exceptionClasses[i];
                String exceptionClassResourceName = exceptionClass.getName().replaceAll("\\.", "/");
                nextException[i] = new Label();
                // If instanceof MyException
                Label l8 = new Label();
                mv.visitLabel(l8);
                mv.visitVarInsn(ALOAD, argRegs + 5); // Cause
                mv.visitTypeInsn(INSTANCEOF, exceptionClassResourceName);
                mv.visitJumpInsn(IFEQ, nextException[i]); // If not, go to ExecutionError
                // If so, throw it
                mv.visitVarInsn(ALOAD, argRegs + 5); // e
                mv.visitTypeInsn(CHECKCAST, exceptionClassResourceName);
                mv.visitInsn(ATHROW);
                mv.visitLabel(nextException[i]);
            }

            // ExecutionError
            mv.visitFrame(
                    Opcodes.F_APPEND, argRegs + 1, new Object[] {
                            "org/simantics/data/session/MethodInterface$ExecutionError", "java/lang/Object" },
                    0, null);
            mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
            mv.visitInsn(DUP);
            mv.visitVarInsn(ALOAD, argRegs + 4);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/Throwable;)V");
            mv.visitInsn(ATHROW);

            // InteruptedException
            mv.visitLabel(l3);
            mv.visitFrame(Opcodes.F_FULL, argRegs + 4,
                    new Object[] { classResourceName, "java/lang/Integer",
                            "org/simantics/data/session/MethodInterface$Method", "[Ljava/lang/Object;",
                            "org/simantics/data/session/MethodInterface$AsyncResult" },
                    1, new Object[] { "java/lang/InterruptedException" });
            mv.visitVarInsn(ASTORE, argRegs + 4);
            Label l10 = new Label();
            mv.visitLabel(l10);
            mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
            mv.visitInsn(DUP);
            mv.visitVarInsn(ALOAD, argRegs + 4);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/Throwable;)V");
            mv.visitInsn(ATHROW);

            Label l11 = new Label();
            mv.visitLabel(l11);
            mv.visitLocalVariable("this", classTypeDescriptor, null, l4, l11, 0);
            //      mv.visitLocalVariable("arg1", "Ljava/lang/Integer;", null, l4, l11, 1);
            register = 1;
            for (int i = 0; i < params.length; i++) {
                Class<?> clazz = params[i];
                mv.visitLocalVariable("arg" + (i + 1), classToTypeDescriptor(clazz), null, l4, l11, register);
                register++;
                if (clazz == long.class || clazz == float.class || clazz == Double.class)
                    argRegs++;
            }
            mv.visitLocalVariable("m", "Lorg/simantics/data/session/MethodInterface$Method;", null, l5, l11,
                    argRegs + 1);
            mv.visitLocalVariable("args", "[Ljava/lang/Object;", null, l6, l11, argRegs + 2);
            mv.visitLocalVariable("result", "Lorg/simantics/data/session/MethodInterface$AsyncResult;", null, l0,
                    l11, argRegs + 3);
            mv.visitLocalVariable("e", "Lorg/simantics/data/session/MethodInterface$ExecutionError;", null, l7, l3,
                    argRegs + 4);
            mv.visitLocalVariable("cause", "Ljava/lang/Object;", null, l7, l3, argRegs + 5);
            mv.visitLocalVariable("e", "Ljava/lang/InterruptedException;", null, l10, l11, argRegs + 4);
            mv.visitMaxs(argRegs + 3, argRegs + 6);
            mv.visitEnd();
            methodNumber++;
        }

        cw.visitEnd();
    }

}