Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 fr.liglab.adele.cilia.dependency; import org.objectweb.asm.*; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** * Generates proxy class delegating operation invocations thanks to a * a dependency. * * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ public class ProxyGenerator implements Opcodes { /** * The dependency name. */ private static final String DEPENDENCY = "m_dependency"; /** * The dependency descriptor. */ private static final String DEPENDENCY_DESC = Type.getDescriptor(Dependency.class); /** * Dependency internal class name. */ private static final String DEPENDENCY_INTERNAL_NAME = "org/apache/felix/ipojo/handlers/dependency/Dependency"; /** * Gets the internal names of the given class objects. * * @param classes the classes * @return the array containing internal names of the given class array. */ private static String[] getInternalClassNames(Class[] classes) { final String[] names = new String[classes.length]; for (int i = 0; i < names.length; i++) { names[i] = Type.getInternalName(classes[i]); } return names; } /** * Generates a proxy class. * * @param spec the proxied service specification * @return the byte[] for the generated proxy class. */ public static byte[] dumpProxy(Class spec) { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); String internalClassName = Type.getInternalName(spec); // Specification class internal name. String[] itfs = new String[0]; String parent = "java/lang/Object"; if (spec.isInterface()) { itfs = new String[] { internalClassName }; // Implemented interface. } else { parent = internalClassName; } String className = internalClassName + "$$Proxy"; // Unique name. // Turn around the VM changes (FELIX-2716) about java.* classes. if (className.startsWith("java/")) { className = "$" + className; } Method[] methods = spec.getMethods(); // Method to delegate cw.visit(Opcodes.V1_3, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, className, null, parent, itfs); addDependencyField(cw); // We try to call super() on the parent, however this should not be used as proxing does work only for interface. generateConstructor(cw, className, parent); // For each method, create the delegator code. for (int i = 0; i < methods.length; i++) { if ((methods[i].getModifiers() & (Modifier.STATIC | Modifier.FINAL)) == 0) { generateDelegator(cw, methods[i], className, internalClassName); } } cw.visitEnd(); return cw.toByteArray(); } /** * Generates a delegated method. * * @param cw the class writer * @param method the method object to delegate * @param className the generated class name * @param itfName the internal specification class name */ private static void generateDelegator(ClassWriter cw, Method method, String className, String itfName) { String methodName = method.getName(); String desc = Type.getMethodDescriptor(method); String[] exceptions = getInternalClassNames(method.getExceptionTypes()); int modifiers = method.getModifiers() & ~(Modifier.ABSTRACT | Modifier.NATIVE | Modifier.SYNCHRONIZED); Type[] types = Type.getArgumentTypes(method); int freeRoom = 1; for (int t = 0; t < types.length; t++) { freeRoom = freeRoom + types[t].getSize(); } MethodVisitor mv = cw.visitMethod(modifiers, methodName, desc, null, exceptions); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, DEPENDENCY, DEPENDENCY_DESC); // The dependency is on the stack. mv.visitMethodInsn(INVOKEVIRTUAL, DEPENDENCY_INTERNAL_NAME, "getService", // Call getService "()Ljava/lang/Object;"); // The service object is on the stack. int varSvc = freeRoom; freeRoom = freeRoom + 1; // Object Reference. mv.visitVarInsn(ASTORE, varSvc); // Store the service object. Label notNull = new Label(); Label isNull = new Label(); mv.visitVarInsn(ALOAD, varSvc); // Load the service mv.visitJumpInsn(IFNONNULL, notNull); // If not null go to not null // Null branch - throw the exception mv.visitLabel(isNull); mv.visitTypeInsn(NEW, "java/lang/RuntimeException"); mv.visitInsn(DUP); mv.visitLdcInsn("No service available"); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V"); mv.visitInsn(ATHROW); // End of the null branch // Not null, go one the execution mv.visitLabel(notNull); // Invoke the method on the service object. mv.visitVarInsn(ALOAD, varSvc); // Push argument on the stack. int i = 1; // Arguments. (non static method) for (int t = 0; t < types.length; t++) { mv.visitVarInsn(types[t].getOpcode(ILOAD), i); i = i + types[t].getSize(); } // Invocation mv.visitMethodInsn(INVOKEINTERFACE, itfName, methodName, desc); // Return the result Type returnType = Type.getReturnType(desc); if (returnType.getSort() != Type.VOID) { mv.visitInsn(returnType.getOpcode(IRETURN)); } else { mv.visitInsn(RETURN); } // End of the method. mv.visitMaxs(0, 0); mv.visitEnd(); } /** * Generates the constructors. The constructor receives a dependency * and set the {@link ProxyGenerator#DEPENDENCY} field. * * @param cw the class writer * @param className the generated class name. */ private static void generateConstructor(ClassWriter cw, String className, String parent) { MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", '(' + DEPENDENCY_DESC + ")V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); // Load this mv.visitInsn(DUP); // Dup mv.visitMethodInsn(INVOKESPECIAL, parent, "<init>", "()V"); // Call super mv.visitVarInsn(ALOAD, 1); // Load the argument mv.visitFieldInsn(PUTFIELD, className, DEPENDENCY, DEPENDENCY_DESC); // Assign the dependency field mv.visitInsn(RETURN); // Return void mv.visitMaxs(0, 0); mv.visitEnd(); } /** * Adds the dependency field {@link ProxyGenerator#DEPENDENCY}. * * @param cw the class writer */ private static void addDependencyField(ClassWriter cw) { cw.visitField(Opcodes.ACC_FINAL, DEPENDENCY, DEPENDENCY_DESC, null, null); cw.visitEnd(); } }