enhancer.examples.generator.proxy.internal.ServiceProxyGenerator.java Source code

Java tutorial

Introduction

Here is the source code for enhancer.examples.generator.proxy.internal.ServiceProxyGenerator.java

Source

/**
 * Copyright (C) 2009 Todor Boev
 *
 * 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 enhancer.examples.generator.proxy.internal;

import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Type.getArgumentTypes;
import static org.objectweb.asm.Type.getInternalName;
import static org.objectweb.asm.Type.getReturnType;

import java.lang.reflect.Method;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.osgi.framework.BundleContext;

import enhancer.Generator;

public class ServiceProxyGenerator implements Generator {
    private static final String INTERNAL = ServiceProxyGenerator.class.getPackage().getName();

    private final String BC = getInternalName(BundleContext.class);
    private final String BC_DESC = "L" + BC + ";";

    private final String HANDLE = getInternalName(ServiceHandle.class);
    private final String HANDLE_DESC = "L" + HANDLE + ";";

    private final String HANDLE_FIELD = "handle";

    @Override
    public boolean isInternal(String name) {
        /*
         * All classes from the same package as the generator are treated as proxy
         * support classes.
         */
        return name.startsWith(INTERNAL);
    }

    @Override
    public byte[] generate(String inputName, String outputName, ClassLoader context) {
        final Class<?> input;
        try {
            input = context.loadClass(inputName);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

        if (!input.isInterface()) {
            throw new RuntimeException("not interface: " + inputName);
        }

        final String proxyName = outputName.replace('.', '/');
        final String base = getInternalName(Object.class);
        final String inputIntName = getInternalName(input);
        final String[] ifaces = new String[] { inputIntName };

        /* Generation start */
        final ClassWriter cw = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS);
        cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, proxyName, null, base, ifaces);

        /* Generate delegate field */
        cw.visitField(ACC_PRIVATE | ACC_FINAL, HANDLE_FIELD, HANDLE_DESC, null, null);

        generateInit(cw, proxyName, inputIntName);

        for (Method method : input.getMethods()) {
            generateMethod(cw, proxyName, method);
        }

        /* Generation end */
        cw.visitEnd();
        return cw.toByteArray();
    }

    private void generateInit(ClassWriter cw, String proxyName, String inputName) {
        /* Generation start */
        final MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(" + BC_DESC + ")V", null, null);
        mv.visitCode();

        /* Call super constructor */
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");

        /* Construct a service handle and store it in a field */
        mv.visitVarInsn(ALOAD, 0);
        mv.visitTypeInsn(NEW, HANDLE);
        mv.visitInsn(DUP);
        mv.visitLdcInsn(Type.getType("L" + inputName + ";"));
        mv.visitVarInsn(ALOAD, 1);
        mv.visitMethodInsn(INVOKESPECIAL, HANDLE, "<init>",
                "(Ljava/lang/Class;Lorg/osgi/framework/BundleContext;)V");
        mv.visitFieldInsn(PUTFIELD, proxyName, HANDLE_FIELD, HANDLE_DESC);

        /* Return */
        mv.visitInsn(RETURN);

        /* Generation end */
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void generateMethod(ClassWriter cw, String proxyName, Method method) {
        final String methName = method.getName();
        final String methDescr = Type.getMethodDescriptor(method);
        final String[] methdExcs = getInternalNames(method.getExceptionTypes());

        /* Generation start */
        final MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methName, methDescr, null, methdExcs);
        mv.visitCode();

        /* Dereference dynamic handle */
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, proxyName, HANDLE_FIELD, HANDLE_DESC);
        mv.visitMethodInsn(INVOKEVIRTUAL, HANDLE, "get", "()Ljava/lang/Object;");

        final Class<?> clazz = method.getDeclaringClass();
        final String subjectName = getInternalName(clazz);

        if (!clazz.isInterface()) {
            mv.visitTypeInsn(CHECKCAST, subjectName);
        }

        /* Load arguments */
        int i = 1;
        for (Type type : getArgumentTypes(method)) {
            mv.visitVarInsn(type.getOpcode(ILOAD), i);
            i += type.getSize();
        }

        /* Invoke */
        if (clazz.isInterface()) {
            mv.visitMethodInsn(INVOKEINTERFACE, subjectName, methName, methDescr);
        } else {
            mv.visitMethodInsn(INVOKEVIRTUAL, subjectName, methName, methDescr);
        }

        /* Return */
        mv.visitInsn(getReturnType(method).getOpcode(IRETURN));

        /* Generation end */
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private static String[] getInternalNames(final Class<?>... clazzes) {
        final String[] names = new String[clazzes.length];
        for (int i = 0; i < names.length; i++) {
            names[i] = getInternalName(clazzes[i]);
        }
        return names;
    }
}