All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xmlbeam.util.intern.ASMHelper Maven / Gradle / Ivy

Go to download

The coolest XML library for Java around. Define typesafe views (projections) to xml. Use XPath to read and write XML. Bind XML to Java collections. Requires at least Java6, supports Java8 features and has no further runtime dependencies.

There is a newer version: 1.4.24
Show newest version
/**
 *  Copyright 2014 Sven Ewald
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.xmlbeam.util.intern;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.xmlbeam.util.intern.org.objectweb.asm.ClassWriter;
import org.xmlbeam.util.intern.org.objectweb.asm.FieldVisitor;
import org.xmlbeam.util.intern.org.objectweb.asm.Label;
import org.xmlbeam.util.intern.org.objectweb.asm.MethodVisitor;
import org.xmlbeam.util.intern.org.objectweb.asm.Opcodes;
import org.xmlbeam.util.intern.org.objectweb.asm.Type;

/**
 * @author Sven Ewald
 */
public class ASMHelper implements Opcodes {

    /**
     * 
     * @param projectionInterface
     * @param delegate
     * @return a new proxy instance forwarding all non default method calls to the delegate
     */
    @SuppressWarnings("unchecked")
    public static  T createDefaultMethodProxy(final Class projectionInterface, final Object delegate) {
        final String proxyClassName = "P" + UUID.randomUUID().toString();

        final Class clazz = new ClassLoader() {
            public Class defineClass(final String name, final byte[] b) {
                return defineClass(name, b, 0, b.length);
            }
        }.defineClass(proxyClassName, getClassData(proxyClassName, projectionInterface));
        T o;

        try {
            final Constructor constructor = clazz.getConstructors()[0];
            o = (T) constructor.newInstance(delegate);
        } catch (final IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (final SecurityException e) {
            throw new RuntimeException(e);
        } catch (final InstantiationException e) {
            throw new RuntimeException(e);
        } catch (final IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (final InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        return o;
    }

    private static byte[] getClassData(final String proxyClassName, final Class projectionInterface) {
        final ClassWriter cw = new ClassWriter(0);
        FieldVisitor fv;

        cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, proxyClassName, null, "java/lang/Object", new String[] { Type.getInternalName(projectionInterface) });

        {
            fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, "handler", Type.getDescriptor(Object.class), null, null);
            fv.visitEnd();
        }
        {
            addConstructorWithParam(proxyClassName, cw, Object.class);
        }

        for (final Method method : ReflectionHelper.getNonDefaultMethods(projectionInterface)) {
            addMethod(proxyClassName, cw, method);
        }
        cw.visitEnd();

        return cw.toByteArray();

    }

    /**
     * @param cw
     * @param ParamClass
     */
    private static void addConstructorWithParam(final String proxyClassName, final ClassWriter cw, final Class ParamClass) {
        final String paramDescriptor = Type.getDescriptor(ParamClass);
        final MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "(" + paramDescriptor + ")V", null, null);
        mv.visitCode();
        final Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V", false);
        final Label l1 = new Label();
        mv.visitLabel(l1);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitFieldInsn(PUTFIELD, proxyClassName, "handler", paramDescriptor);
        final Label l2 = new Label();
        mv.visitLabel(l2);
        mv.visitInsn(RETURN);
        final Label l3 = new Label();
        mv.visitLabel(l3);
        mv.visitMaxs(2, 2);
        mv.visitEnd();
    }

    /**
     * @param cw
     * @param method
     */
    private static void addMethod(final String proxyClassName, final ClassWriter cw, final Method method) {
        MethodVisitor mv;
        final String methodDescriptor = Type.getMethodDescriptor(method);
        mv = cw.visitMethod(ACC_PUBLIC, method.getName(), methodDescriptor, null, null);

        mv.visitCode();
        final Label l0 = new Label();
        mv.visitLabel(l0);

        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, proxyClassName, "handler", Type.getDescriptor(Object.class));
//        mv.visitInsn(ACONST_NULL);
//        mv.visitInsn(ACONST_NULL);
        //       mv.visitMethodInsn(INVOKEINTERFACE, proxyClassName, "asmInvoke", "(Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
        //   mv.visitInsn(ICONST_0);
        //   mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");

//        mv.visitVarInsn(ALOAD, 0);
//        mv.visitFieldInsn(GETFIELD, "org/xmlbeam/tests/util/intern/TestASMProxy$1", "handler", "Lorg/xmlbeam/tests/util/intern/TestASMProxy$ProxyMe;");
        //  mv.visitMethodInsn(INVOKEINTERFACE, "org/xmlbeam/tests/util/intern/TestASMProxy$ProxyMe", "invokeMePlz", "()I", true);
        int c = 1;
        for (final Class param : method.getParameterTypes()) {
            mv.visitVarInsn(getLoadOpcodeForType(param), c++);
        }
        mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(method.getDeclaringClass()), method.getName(), Type.getMethodDescriptor(method), true);

        if (ReflectionHelper.hasReturnType(method)) {
            final Class returnType = method.getReturnType();
            //  mv.visitTypeInsn(CHECKCAST, Type.getInternalName(returnType));
            //    if (returnType.isPrimitive()) {
            //        autoUnBoxing(mv, Type.getType(returnType));
            //    }
            mv.visitInsn(getReturnOpcodeForType(returnType));
        } else {
            mv.visitInsn(POP);
            mv.visitInsn(RETURN);
        }
        final Label l1 = new Label();
        mv.visitLabel(l1);
        mv.visitMaxs(c + 1, c + 1);
        mv.visitEnd();

    }

//    protected static void autoUnBoxing(final MethodVisitor mv, final Type fieldType) {
//        switch (fieldType.getSort()) {
//        case Type.BOOLEAN:
//            mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
//            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
//            break;
//        case Type.BYTE:
//            mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
//            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
//            break;
//        case Type.CHAR:
//            mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
//            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
//            break;
//        case Type.SHORT:
//            mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
//            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
//            break;
//        case Type.INT:
//            mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
//            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
//            break;
//        case Type.FLOAT:
//            mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
//            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
//            break;
//        case Type.LONG:
//            mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
//            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
//            break;
//        case Type.DOUBLE:
//            mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
//            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
//            break;
//        case Type.ARRAY:
//            mv.visitTypeInsn(CHECKCAST, fieldType.getInternalName());
//            break;
//        default:
//            mv.visitTypeInsn(CHECKCAST, fieldType.getInternalName());
//        }
//    }

    private final static Map, Integer> LOADOPCODE = new HashMap, Integer>();
    static {
        LOADOPCODE.put(Boolean.TYPE, ILOAD);
        LOADOPCODE.put(Byte.TYPE, ILOAD);
        LOADOPCODE.put(Character.TYPE, ILOAD);
        LOADOPCODE.put(Short.TYPE, ILOAD);
        LOADOPCODE.put(Integer.TYPE, ILOAD);
        LOADOPCODE.put(Float.TYPE, FLOAD);
        LOADOPCODE.put(Long.TYPE, LLOAD);
        LOADOPCODE.put(Double.TYPE, DLOAD);
    }

    /**
     * @param param
     * @return
     */
    private static int getLoadOpcodeForType(final Class param) {
        if (LOADOPCODE.containsKey(param)) {
            return LOADOPCODE.get(param);
        }
        return ALOAD;
    }

    /**
     * @param returnType
     * @return
     */
    private static int getReturnOpcodeForType(final Class returnType) {
        switch (Type.getType(returnType).getSort()) {
        case Type.BOOLEAN:
        case Type.BYTE:
        case Type.CHAR:
        case Type.SHORT:
        case Type.INT:
            return IRETURN;
        case Type.FLOAT:
            return FRETURN;
        case Type.LONG:
            return LRETURN;
        case Type.DOUBLE:
            return DRETURN;
        default:
            return ARETURN;
        }
    }

//    protected static void autoBoxing(final MethodVisitor mv, final Type fieldType) {
//        switch (fieldType.getSort()) {
//        case Type.BOOLEAN:
//            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
//            break;
//        case Type.BYTE:
//            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
//            break;
//        case Type.CHAR:
//            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
//            break;
//        case Type.SHORT:
//            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
//            break;
//        case Type.INT:
//            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
//            break;
//        case Type.FLOAT:
//            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
//            break;
//        case Type.LONG:
//            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
//            break;
//        case Type.DOUBLE:
//            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
//            break;
//        }
//    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy