| /* |
| * 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 org.apache.aries.proxy.impl.gen; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.Modifier; |
| |
| import org.apache.aries.proxy.impl.NLS; |
| import org.apache.aries.proxy.impl.ProxyUtils; |
| import org.apache.aries.proxy.impl.SystemModuleClassLoader; |
| 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.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.Type; |
| import org.objectweb.asm.commons.GeneratorAdapter; |
| import org.objectweb.asm.commons.Method; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public class ProxySubclassAdapter extends ClassVisitor implements Opcodes |
| { |
| |
| private static final Type STRING_TYPE = Type.getType(String.class); |
| private static final Type CLASS_TYPE = Type.getType(Class.class); |
| private static final Type CLASSLOADER_TYPE = Type.getType(ClassLoader.class); |
| private static final Type OBJECT_TYPE = Type.getType(Object.class); |
| private static final Type METHOD_TYPE = Type.getType(java.lang.reflect.Method.class); |
| private static final Type IH_TYPE = Type.getType(InvocationHandler.class); |
| private static final Type[] NO_ARGS = new Type[] {}; |
| |
| private static final String IH_FIELD = "ih"; |
| |
| private static Logger LOGGER = LoggerFactory.getLogger(ProxySubclassAdapter.class); |
| |
| private String newClassName = null; |
| private String superclassBinaryName = null; |
| private Class<?> superclassClass = null; |
| private ClassLoader loader = null; |
| private Type newClassType = null; |
| private GeneratorAdapter staticAdapter = null; |
| private String currentlyAnalysedClassName = null; |
| private Class<?> currentlyAnalysedClass = null; |
| private String currentClassFieldName = null; |
| |
| public ProxySubclassAdapter(ClassVisitor writer, String newClassName, ClassLoader loader) |
| { |
| // call the superclass constructor |
| super(Opcodes.ASM5, writer); |
| // the writer is now the cv in the superclass of ClassAdapter |
| |
| LOGGER.debug(Constants.LOG_ENTRY, "ProxySubclassAdapter", new Object[] { this, writer, |
| newClassName }); |
| |
| // set the newClassName field |
| this.newClassName = newClassName; |
| // set the newClassType descriptor |
| newClassType = Type.getType("L" + newClassName + ";"); |
| |
| // set the classloader |
| this.loader = loader; |
| |
| LOGGER.debug(Constants.LOG_EXIT, "ProxySubclassAdapter", this); |
| } |
| |
| /* |
| * This method visits the class to generate the new subclass. |
| * |
| * The following things happen here: 1. The class is renamed to a dynamic |
| * name 2. The existing class name is changed to be the superclass name so |
| * that the generated class extends the original class. 3. A private field |
| * is added to store an invocation handler 4. A constructor is added that |
| * takes an invocation handler as an argument 5. The constructor method |
| * instantiates an instance of the superclass 6. The constructor method sets |
| * the invocation handler so the invoke method can be called from all the |
| * subsequently rewritten methods 7. Add a getInvocationHandler() method 8. |
| * store a static Class object of the superclass so we can reflectively find |
| * methods later |
| */ |
| public void visit(int version, int access, String name, String signature, String superName, |
| String[] interfaces) |
| { |
| LOGGER.debug(Constants.LOG_ENTRY, "visit", new Object[] { version, access, name, |
| signature, superName, interfaces }); |
| |
| // store the superclass binary name |
| this.superclassBinaryName = name.replaceAll("/", "\\."); |
| |
| try { |
| this.superclassClass = Class.forName(superclassBinaryName, false, loader); |
| } catch (ClassNotFoundException cnfe) { |
| throw new TypeNotPresentException(superclassBinaryName, cnfe); |
| } |
| |
| |
| // keep the same access and signature as the superclass (unless it's abstract) |
| // remove all the superclass interfaces because they will be inherited |
| // from the superclass anyway |
| if((access & ACC_ABSTRACT) != 0) { |
| //If the super was abstract the subclass should not be! |
| access &= ~ACC_ABSTRACT; |
| } |
| cv.visit(ProxyUtils.getWeavingJavaVersion(), access, newClassName, signature, name, null); |
| |
| // add a private field for the invocation handler |
| // this isn't static in case we have multiple instances of the same |
| // proxy |
| cv.visitField(ACC_PRIVATE, IH_FIELD, Type.getDescriptor(InvocationHandler.class), null, null); |
| |
| // create a static adapter for generating a static initialiser method in |
| // the generated subclass |
| staticAdapter = new GeneratorAdapter(ACC_STATIC, |
| new Method("<clinit>", Type.VOID_TYPE, NO_ARGS), null, null, cv); |
| |
| // add a zero args constructor method |
| Method m = new Method("<init>", Type.VOID_TYPE, NO_ARGS); |
| GeneratorAdapter methodAdapter = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cv); |
| // loadthis |
| methodAdapter.loadThis(); |
| // List the constructors in the superclass. |
| Constructor<?>[] constructors = superclassClass.getDeclaredConstructors(); |
| // Check that we've got at least one constructor, and get the 1st one in the list. |
| if (constructors.length > 0) { |
| // We now need to construct the proxy class as though it is going to invoke the superclasses constructor. |
| // We do this because we can no longer call the java.lang.Object() zero arg constructor as the JVM now throws a VerifyError. |
| // So what we do is build up the calling of the superclasses constructor using nulls and default values. This means that the |
| // class bytes can be verified by the JVM, and then in the ProxySubclassGenerator, we load the class without invoking the |
| // constructor. |
| Method constructor = Method.getMethod(constructors[0]); |
| Type[] argTypes = constructor.getArgumentTypes(); |
| if (argTypes.length == 0) { |
| methodAdapter.invokeConstructor(Type.getType(superclassClass), new Method("<init>", Type.VOID_TYPE, NO_ARGS)); |
| } else { |
| for (Type type : argTypes) { |
| switch (type.getSort()) |
| { |
| case Type.ARRAY: |
| // We need to process any array or multidimentional arrays. |
| String elementDesc = type.getElementType().getDescriptor(); |
| String typeDesc = type.getDescriptor(); |
| |
| // Iterate over the number of arrays and load 0 for each one. Keep a count of the number of |
| // arrays as we will need to run different code fo multi dimentional arrays. |
| int index = 0; |
| while (! elementDesc.equals(typeDesc)) { |
| typeDesc = typeDesc.substring(1); |
| methodAdapter.visitInsn(Opcodes.ICONST_0); |
| index++; |
| } |
| // If we're just a single array, then call the newArray method, otherwise use the MultiANewArray instruction. |
| if (index == 1) { |
| methodAdapter.newArray(type.getElementType()); |
| } else { |
| methodAdapter.visitMultiANewArrayInsn(type.getDescriptor(), index); |
| } |
| break; |
| case Type.BOOLEAN: |
| methodAdapter.push(true); |
| break; |
| case Type.BYTE: |
| methodAdapter.push(Type.VOID_TYPE); |
| break; |
| case Type.CHAR: |
| methodAdapter.push(Type.VOID_TYPE); |
| break; |
| case Type.DOUBLE: |
| methodAdapter.push(0.0); |
| break; |
| case Type.FLOAT: |
| methodAdapter.push(0.0f); |
| break; |
| case Type.INT: |
| methodAdapter.push(0); |
| break; |
| case Type.LONG: |
| methodAdapter.push(0l); |
| break; |
| case Type.SHORT: |
| methodAdapter.push(0); |
| break; |
| default: |
| case Type.OBJECT: |
| methodAdapter.visitInsn(Opcodes.ACONST_NULL); |
| break; |
| } |
| } |
| |
| methodAdapter.invokeConstructor(Type.getType(superclassClass), new Method("<init>", Type.VOID_TYPE, argTypes)); |
| } |
| } |
| methodAdapter.returnValue(); |
| methodAdapter.endMethod(); |
| |
| // add a method for getting the invocation handler |
| Method setter = new Method("setInvocationHandler", Type.VOID_TYPE, new Type[] { IH_TYPE }); |
| m = new Method("getInvocationHandler", IH_TYPE, NO_ARGS); |
| methodAdapter = new GeneratorAdapter(ACC_PUBLIC | ACC_FINAL, m, null, null, cv); |
| // load this to get the field |
| methodAdapter.loadThis(); |
| // get the ih field and return |
| methodAdapter.getField(newClassType, IH_FIELD, IH_TYPE); |
| methodAdapter.returnValue(); |
| methodAdapter.endMethod(); |
| |
| // add a method for setting the invocation handler |
| methodAdapter = new GeneratorAdapter(ACC_PUBLIC | ACC_FINAL, setter, null, null, cv); |
| // load this to put the field |
| methodAdapter.loadThis(); |
| // load the method arguments (i.e. the invocation handler) to the stack |
| methodAdapter.loadArgs(); |
| // set the ih field using the method argument |
| methodAdapter.putField(newClassType, IH_FIELD, IH_TYPE); |
| methodAdapter.returnValue(); |
| methodAdapter.endMethod(); |
| |
| // loop through the class hierarchy to get any needed methods off the |
| // supertypes |
| // start by finding the methods declared on the class of interest (the |
| // superclass of our dynamic subclass) |
| java.lang.reflect.Method[] observedMethods = superclassClass.getDeclaredMethods(); |
| // add the methods to a set of observedMethods |
| ProxySubclassMethodHashSet<String> setOfObservedMethods = new ProxySubclassMethodHashSet<String>( |
| observedMethods.length); |
| setOfObservedMethods.addMethodArray(observedMethods); |
| // get the next superclass in the hierarchy |
| Class<?> nextSuperClass = superclassClass.getSuperclass(); |
| while (nextSuperClass != null) { |
| // set the fields for the current class |
| setCurrentAnalysisClassFields(nextSuperClass); |
| |
| // add a static field and static initializer code to the generated |
| // subclass |
| // for each of the superclasses in the hierarchy |
| addClassStaticField(currentlyAnalysedClassName); |
| |
| LOGGER.debug("Class currently being analysed: {} {}", currentlyAnalysedClassName, |
| currentlyAnalysedClass); |
| |
| // now find the methods declared on the current class and add them |
| // to a set of foundMethods |
| java.lang.reflect.Method[] foundMethods = currentlyAnalysedClass.getDeclaredMethods(); |
| ProxySubclassMethodHashSet<String> setOfFoundMethods = new ProxySubclassMethodHashSet<String>( |
| foundMethods.length); |
| setOfFoundMethods.addMethodArray(foundMethods); |
| // remove from the set of foundMethods any methods we saw on a |
| // subclass |
| // because we want to use the lowest level declaration of a method |
| setOfFoundMethods.removeAll(setOfObservedMethods); |
| try { |
| // read the current class and use a |
| // ProxySubclassHierarchyAdapter |
| // to process only methods on that class that are in the list |
| ClassLoader loader = currentlyAnalysedClass.getClassLoader(); |
| if (loader == null) { |
| loader = this.loader; |
| } |
| InputStream is = loader.getResourceAsStream(currentlyAnalysedClass |
| .getName().replaceAll("\\.", "/") |
| + ".class"); |
| if (is == null) { |
| //use SystemModuleClassLoader as fallback |
| ClassLoader classLoader = new SystemModuleClassLoader(); |
| is = classLoader.getResourceAsStream(currentlyAnalysedClass |
| .getName().replaceAll("\\.", "/") |
| + ".class"); |
| } |
| ClassReader cr = new ClassReader(is); |
| ClassVisitor hierarchyAdapter = new ProxySubclassHierarchyAdapter(this, setOfFoundMethods); |
| cr.accept(hierarchyAdapter, ClassReader.SKIP_DEBUG); |
| } catch (IOException e) { |
| throw new TypeNotPresentException(currentlyAnalysedClassName, e); |
| } |
| // now add the foundMethods to the overall list of observed methods |
| setOfObservedMethods.addAll(setOfFoundMethods); |
| // get the next class up in the hierarchy and go again |
| nextSuperClass = currentlyAnalysedClass.getSuperclass(); |
| } |
| |
| // we've finished looking at the superclass hierarchy |
| // set the fields for the immediate superclass of our dynamic subclass |
| setCurrentAnalysisClassFields(superclassClass); |
| |
| // add the class static field |
| addClassStaticField(currentlyAnalysedClassName); |
| // we do the lowest class last because we are already visiting the class |
| // when in this adapter code |
| // now we are ready to visit all the methods on the lowest class |
| // which will happen by the ASM ClassVisitor implemented in this adapter |
| |
| LOGGER.debug(Constants.LOG_EXIT, "visit"); |
| } |
| |
| public void visitSource(String source, String debug) |
| { |
| LOGGER.debug(Constants.LOG_ENTRY, "visitSource", new Object[] { source, debug }); |
| |
| // set the source to null since the class is generated on the fly and |
| // not compiled |
| cv.visitSource(null, null); |
| |
| LOGGER.debug(Constants.LOG_EXIT, "visitSource"); |
| } |
| |
| public void visitEnd() |
| { |
| LOGGER.debug(Constants.LOG_ENTRY, "visitEnd"); |
| |
| // this method is called when we reach the end of the class |
| // so it is time to make sure the static initialiser method is closed |
| staticAdapter.returnValue(); |
| staticAdapter.endMethod(); |
| // now delegate to the cv |
| cv.visitEnd(); |
| |
| LOGGER.debug(Constants.LOG_EXIT, "visitEnd"); |
| } |
| |
| /* |
| * This method is called on each method of the superclass (and all parent |
| * classes up to Object) Each of these methods is visited in turn and the |
| * code here generates the byte code for the InvocationHandler to call the |
| * methods on the superclass. |
| */ |
| public MethodVisitor visitMethod(int access, String name, String desc, String signature, |
| String[] exceptions) |
| { |
| LOGGER.debug(Constants.LOG_ENTRY, "visitMethod", new Object[] { access, name, desc, |
| signature, exceptions }); |
| |
| /* |
| * Check the method access and handle the method types we don't want to |
| * copy: final methods (issue warnings if these are not methods from |
| * java.* classes) static methods (initialiser and others) private |
| * methods, constructors (for now we don't copy any constructors) |
| * everything else we process to proxy. Abstract methods should be made |
| * non-abstract so that they can be proxied. |
| */ |
| |
| if((access & ACC_ABSTRACT) != 0) { |
| //If the method is abstract then it should not be in the concrete subclass! |
| access &= ~ACC_ABSTRACT; |
| } |
| |
| LOGGER.debug("Method name: {} with descriptor: {}", name, desc); |
| |
| MethodVisitor methodVisitorToReturn = null; |
| |
| if (name.equals("<init>")) { |
| // we may need to do something extra with constructors later |
| // e.g. include bytecode for calling super with the same args |
| // since we currently rely on the super having a zero args |
| // constructor |
| // we need to issue an error if we don't find one |
| |
| // for now we return null to ignore them |
| methodVisitorToReturn = null; |
| } else if (name.equals("<clinit>")) { |
| // don't copy static initialisers from the superclass into the new |
| // subclass |
| methodVisitorToReturn = null; |
| } else if ((access & ACC_FINAL) != 0) { |
| // since we check for final methods in the ProxySubclassGenerator we |
| // should never get here |
| methodVisitorToReturn = null; |
| } else if ((access & ACC_SYNTHETIC) != 0) { |
| // synthetic methods are generated by the compiler for covariance |
| // etc |
| // we shouldn't copy them or we will have duplicate methods |
| methodVisitorToReturn = null; |
| } else if ((access & ACC_PRIVATE) != 0) { |
| // don't copy private methods from the superclass |
| methodVisitorToReturn = null; |
| } else if ((access & ACC_STATIC) != 0) { |
| // don't copy static methods |
| methodVisitorToReturn = null; |
| } else if (!(((access & ACC_PUBLIC) != 0) || ((access & ACC_PROTECTED) != 0) || ((access & ACC_PRIVATE) != 0))) { |
| // the default (package) modifier value is 0, so by using & with any |
| // of the other |
| // modifier values and getting a result of zero means that we have |
| // default accessibility |
| |
| // check the package in which the method is declared against the |
| // package |
| // where the generated subclass will be |
| // if they are the same process the method otherwise ignore it |
| if (currentlyAnalysedClass.getPackage().equals(superclassClass.getPackage())) { |
| processMethod(access, name, desc, signature, exceptions); |
| methodVisitorToReturn = null; |
| } else { |
| methodVisitorToReturn = null; |
| } |
| } else { |
| processMethod(access, name, desc, signature, exceptions); |
| // return null because we don't want the original method code from |
| // the superclass |
| methodVisitorToReturn = null; |
| } |
| |
| LOGGER.debug(Constants.LOG_EXIT, "visitMethod", methodVisitorToReturn); |
| |
| return methodVisitorToReturn; |
| |
| } |
| |
| private void processMethod(int access, String name, String desc, String signature, |
| String[] exceptions) |
| { |
| LOGGER.debug(Constants.LOG_ENTRY, "processMethod", new Object[] { access, name, desc, |
| signature, exceptions }); |
| |
| LOGGER.debug("Processing method: {} with descriptor {}", name, desc); |
| |
| // identify the target method parameters and return type |
| Method currentTransformMethod = new Method(name, desc); |
| Type[] targetMethodParameters = currentTransformMethod.getArgumentTypes(); |
| Type returnType = currentTransformMethod.getReturnType(); |
| |
| // we create a static field for each method we encounter with a name |
| // like method_parm1_parm2... |
| StringBuilder methodStaticFieldNameBuilder = new StringBuilder(name); |
| // for each a parameter get the name and add it to the field removing |
| // the dots first |
| for (Type t : targetMethodParameters) { |
| methodStaticFieldNameBuilder.append("_"); |
| methodStaticFieldNameBuilder.append(t.getClassName().replaceAll("\\[\\]", "Array") |
| .replaceAll("\\.", "")); |
| } |
| String methodStaticFieldName = methodStaticFieldNameBuilder.toString(); |
| |
| // add a private static field for the method |
| cv.visitField(ACC_PRIVATE | ACC_STATIC, methodStaticFieldName, METHOD_TYPE.getDescriptor(), |
| null, null); |
| |
| // visit the method using the class writer, delegated through the method |
| // visitor and generator |
| // modify the method access so that any native methods aren't |
| // described as native |
| // since they won't be native in proxy form |
| // also stop methods being marked synchronized on the proxy as they will |
| // be sync |
| // on the real object |
| int newAccess = access & (~ACC_NATIVE) & (~ACC_SYNCHRONIZED); |
| MethodVisitor mv = cv.visitMethod(newAccess, name, desc, signature, exceptions); |
| // use a GeneratorAdapter to build the invoke call directly in byte code |
| GeneratorAdapter methodAdapter = new GeneratorAdapter(mv, newAccess, name, desc); |
| |
| /* |
| * Stage 1 creates the bytecode for adding the reflected method of the |
| * superclass to a static field in the subclass: private static Method |
| * methodName_parm1_parm2... = null; static{ methodName_parm1_parm2... = |
| * superClass.getDeclaredMethod(methodName,new Class[]{method args}; } |
| * |
| * Stage 2 is to call the ih.invoke(this,methodName_parm1_parm2,args) in |
| * the new subclass methods Stage 3 is to cast the return value to the |
| * correct type |
| */ |
| |
| /* |
| * Stage 1 use superClass.getMethod(methodName,new Class[]{method args} |
| * from the Class object on the stack |
| */ |
| |
| // load the static superclass Class onto the stack |
| staticAdapter.getStatic(newClassType, currentClassFieldName, CLASS_TYPE); |
| |
| // push the method name string arg onto the stack |
| staticAdapter.push(name); |
| |
| // create an array of the method parm class[] arg |
| staticAdapter.push(targetMethodParameters.length); |
| staticAdapter.newArray(CLASS_TYPE); |
| int index = 0; |
| for (Type t : targetMethodParameters) { |
| staticAdapter.dup(); |
| staticAdapter.push(index); |
| switch (t.getSort()) |
| { |
| case Type.BOOLEAN: |
| staticAdapter.getStatic(Type.getType(java.lang.Boolean.class), "TYPE", CLASS_TYPE); |
| break; |
| case Type.BYTE: |
| staticAdapter.getStatic(Type.getType(java.lang.Byte.class), "TYPE", CLASS_TYPE); |
| break; |
| case Type.CHAR: |
| staticAdapter.getStatic(Type.getType(java.lang.Character.class), "TYPE", CLASS_TYPE); |
| break; |
| case Type.DOUBLE: |
| staticAdapter.getStatic(Type.getType(java.lang.Double.class), "TYPE", CLASS_TYPE); |
| break; |
| case Type.FLOAT: |
| staticAdapter.getStatic(Type.getType(java.lang.Float.class), "TYPE", CLASS_TYPE); |
| break; |
| case Type.INT: |
| staticAdapter.getStatic(Type.getType(java.lang.Integer.class), "TYPE", CLASS_TYPE); |
| break; |
| case Type.LONG: |
| staticAdapter.getStatic(Type.getType(java.lang.Long.class), "TYPE", CLASS_TYPE); |
| break; |
| case Type.SHORT: |
| staticAdapter.getStatic(Type.getType(java.lang.Short.class), "TYPE", CLASS_TYPE); |
| break; |
| default: |
| case Type.OBJECT: |
| staticAdapter.push(t); |
| break; |
| } |
| staticAdapter.arrayStore(CLASS_TYPE); |
| index++; |
| } |
| |
| // invoke the getMethod |
| staticAdapter.invokeVirtual(CLASS_TYPE, new Method("getDeclaredMethod", METHOD_TYPE, |
| new Type[] { STRING_TYPE, Type.getType(java.lang.Class[].class) })); |
| |
| // store the reflected method in the static field |
| staticAdapter.putStatic(newClassType, methodStaticFieldName, METHOD_TYPE); |
| |
| /* |
| * Stage 2 call the ih.invoke(this,supermethod,parms) |
| */ |
| |
| // load this to get the ih field |
| methodAdapter.loadThis(); |
| // load the invocation handler from the field (the location of the |
| // InvocationHandler.invoke) |
| methodAdapter.getField(newClassType, IH_FIELD, IH_TYPE); |
| // loadThis (the first arg of the InvocationHandler.invoke) |
| methodAdapter.loadThis(); |
| // load the method to invoke (the second arg of the |
| // InvocationHandler.invoke) |
| methodAdapter.getStatic(newClassType, methodStaticFieldName, METHOD_TYPE); |
| // load all the method arguments onto the stack as an object array (the |
| // third arg of the InvocationHandler.invoke) |
| methodAdapter.loadArgArray(); |
| // generate the invoke method |
| Method invocationHandlerInvokeMethod = new Method("invoke", OBJECT_TYPE, new Type[] { |
| OBJECT_TYPE, METHOD_TYPE, Type.getType(java.lang.Object[].class) }); |
| // call the invoke method of the invocation handler |
| methodAdapter.invokeInterface(IH_TYPE, invocationHandlerInvokeMethod); |
| |
| /* |
| * Stage 3 the returned object is now on the top of the stack We need to |
| * check the type and cast as necessary |
| */ |
| switch (returnType.getSort()) |
| { |
| case Type.BOOLEAN: |
| methodAdapter.cast(OBJECT_TYPE, Type.getType(Boolean.class)); |
| methodAdapter.unbox(Type.BOOLEAN_TYPE); |
| break; |
| case Type.BYTE: |
| methodAdapter.cast(OBJECT_TYPE, Type.getType(Byte.class)); |
| methodAdapter.unbox(Type.BYTE_TYPE); |
| break; |
| case Type.CHAR: |
| methodAdapter.cast(OBJECT_TYPE, Type.getType(Character.class)); |
| methodAdapter.unbox(Type.CHAR_TYPE); |
| break; |
| case Type.DOUBLE: |
| methodAdapter.cast(OBJECT_TYPE, Type.getType(Double.class)); |
| methodAdapter.unbox(Type.DOUBLE_TYPE); |
| break; |
| case Type.FLOAT: |
| methodAdapter.cast(OBJECT_TYPE, Type.getType(Float.class)); |
| methodAdapter.unbox(Type.FLOAT_TYPE); |
| break; |
| case Type.INT: |
| methodAdapter.cast(OBJECT_TYPE, Type.getType(Integer.class)); |
| methodAdapter.unbox(Type.INT_TYPE); |
| break; |
| case Type.LONG: |
| methodAdapter.cast(OBJECT_TYPE, Type.getType(Long.class)); |
| methodAdapter.unbox(Type.LONG_TYPE); |
| break; |
| case Type.SHORT: |
| methodAdapter.cast(OBJECT_TYPE, Type.getType(Short.class)); |
| methodAdapter.unbox(Type.SHORT_TYPE); |
| break; |
| case Type.VOID: |
| methodAdapter.cast(OBJECT_TYPE, Type.getType(Void.class)); |
| methodAdapter.unbox(Type.VOID_TYPE); |
| break; |
| default: |
| case Type.OBJECT: |
| // in this case check the cast and cast the object to the return |
| // type |
| methodAdapter.checkCast(returnType); |
| methodAdapter.cast(OBJECT_TYPE, returnType); |
| break; |
| } |
| // return the (appropriately cast) result of the invocation from the |
| // stack |
| methodAdapter.returnValue(); |
| // end the method |
| methodAdapter.endMethod(); |
| |
| LOGGER.debug(Constants.LOG_EXIT, "processMethod"); |
| } |
| |
| private void addClassStaticField(String classBinaryName) |
| { |
| LOGGER.debug(Constants.LOG_ENTRY, "addClassStaticField", |
| new Object[] { classBinaryName }); |
| |
| currentClassFieldName = classBinaryName.replaceAll("\\.", "_"); |
| |
| /* |
| * use Class.forName on the superclass so we can reflectively find |
| * methods later |
| * |
| * produces bytecode for retrieving the superclass and storing in a |
| * private static field: private static Class superClass = null; static{ |
| * superClass = Class.forName(superclass, true, TYPE_BEING_PROXIED.class.getClassLoader()); } |
| */ |
| |
| // add a private static field for the superclass Class |
| cv.visitField(ACC_PRIVATE | ACC_STATIC, currentClassFieldName, CLASS_TYPE.getDescriptor(), |
| null, null); |
| |
| // push the String arg for the Class.forName onto the stack |
| staticAdapter.push(classBinaryName); |
| //push the boolean arg for the Class.forName onto the stack |
| staticAdapter.push(true); |
| //get the classloader |
| staticAdapter.push(newClassType); |
| staticAdapter.invokeVirtual(CLASS_TYPE, new Method("getClassLoader", CLASSLOADER_TYPE, NO_ARGS)); |
| |
| // invoke the Class forName putting the Class on the stack |
| staticAdapter.invokeStatic(CLASS_TYPE, new Method("forName", CLASS_TYPE, |
| new Type[] { STRING_TYPE, Type.BOOLEAN_TYPE, CLASSLOADER_TYPE })); |
| |
| // put the Class in the static field |
| staticAdapter.putStatic(newClassType, currentClassFieldName, CLASS_TYPE); |
| |
| LOGGER.debug(Constants.LOG_ENTRY, "addClassStaticField"); |
| } |
| |
| private void setCurrentAnalysisClassFields(Class<?> aClass) |
| { |
| LOGGER.debug(Constants.LOG_ENTRY, "setCurrentAnalysisClassFields", |
| new Object[] { aClass }); |
| |
| currentlyAnalysedClassName = aClass.getName(); |
| currentlyAnalysedClass = aClass; |
| |
| LOGGER.debug(Constants.LOG_EXIT, "setCurrentAnalysisClassFields"); |
| } |
| |
| // we don't want to copy fields from the class into the proxy |
| public FieldVisitor visitField(int access, String name, String desc, String signature, |
| Object value) |
| { |
| return null; |
| } |
| |
| // for now we don't do any processing in these methods |
| public AnnotationVisitor visitAnnotation(String desc, boolean visible) |
| { |
| return null; |
| } |
| |
| public void visitAttribute(Attribute attr) |
| { |
| // no-op |
| } |
| |
| public void visitInnerClass(String name, String outerName, String innerName, int access) |
| { |
| // no-op |
| } |
| |
| public void visitOuterClass(String owner, String name, String desc) |
| { |
| // no-op |
| } |
| } |