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

org.apache.openejb.util.AsmParameterNameLoader Maven / Gradle / Ivy

There is a newer version: 4.7.5
Show newest version
/*
 * 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.openejb.util;

import org.apache.xbean.asm.ClassReader;
import org.apache.xbean.asm.Label;
import org.apache.xbean.asm.MethodVisitor;
import org.apache.xbean.asm.Type;
import org.apache.xbean.asm.commons.EmptyVisitor;
import org.apache.xbean.recipe.ParameterNameLoader;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.security.AccessController;
import java.security.PrivilegedAction;


/**
 * Implementation of ParameterNameLoader that uses ASM to read the parameter names from the local variable table in the
 * class byte code.
 * 

* This wonderful piece of code was taken from org.springframework.core.LocalVariableTableParameterNameDiscover */ public class AsmParameterNameLoader implements ParameterNameLoader { public static void install(){ AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { Field field = org.apache.xbean.recipe.ReflectionUtil.class.getDeclaredField("parameterNamesLoader"); field.setAccessible(true); field.set(null, new AsmParameterNameLoader()); // if (field.get(null) == null){ // } } catch (Exception e) { e.printStackTrace(); } return null; } }); } /** * Weak map from Constructor to List<String>. */ private final WeakHashMap> constructorCache = new WeakHashMap>(); /** * Weak map from Method to List<String>. */ private final WeakHashMap> methodCache = new WeakHashMap>(); /** * Gets the parameter names of the specified method or null if the class was compiled without debug symbols on. * * @param method the method for which the parameter names should be retrieved * @return the parameter names or null if the class was compilesd without debug symbols on */ public List get(Method method) { // check the cache if (methodCache.containsKey(method)) { return methodCache.get(method); } Map> allMethodParameters = getAllMethodParameters(method.getDeclaringClass(), method.getName()); return allMethodParameters.get(method); } /** * Gets the parameter names of the specified constructor or null if the class was compiled without debug symbols on. * * @param constructor the constructor for which the parameters should be retrieved * @return the parameter names or null if the class was compiled without debug symbols on */ public List get(Constructor constructor) { // check the cache if (constructorCache.containsKey(constructor)) { return constructorCache.get(constructor); } Map> allConstructorParameters = getAllConstructorParameters(constructor.getDeclaringClass()); return allConstructorParameters.get(constructor); } /** * Gets the parameter names of all constructoror null if the class was compiled without debug symbols on. * * @param clazz the class for which the constructor parameter names should be retrieved * @return a map from Constructor object to the parameter names or null if the class was compiled without debug symbols on */ public Map> getAllConstructorParameters(Class clazz) { // Determine the constructors? List constructors = new ArrayList(Arrays.asList(clazz.getConstructors())); constructors.addAll(Arrays.asList(clazz.getDeclaredConstructors())); if (constructors.isEmpty()) { return Collections.emptyMap(); } // Check the cache if (constructorCache.containsKey(constructors.get(0))) { Map> constructorParameters = new HashMap>(); for (Constructor constructor : constructors) { constructorParameters.put(constructor, constructorCache.get(constructor)); } return constructorParameters; } // Load the parameter names using ASM Map> constructorParameters = new HashMap>(); try { ClassReader reader = AsmParameterNameLoader.createClassReader(clazz); AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor visitor = new AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor(clazz); reader.accept(visitor, 0); Map exceptions = visitor.getExceptions(); if (exceptions.size() == 1) { throw new RuntimeException((Exception) exceptions.values().iterator().next()); } if (!exceptions.isEmpty()) { throw new RuntimeException(exceptions.toString()); } constructorParameters = visitor.getConstructorParameters(); } catch (IOException ex) { } // Cache the names for (Constructor constructor : constructors) { constructorCache.put(constructor, constructorParameters.get(constructor)); } return constructorParameters; } /** * Gets the parameter names of all methods with the specified name or null if the class was compiled without debug symbols on. * * @param clazz the class for which the method parameter names should be retrieved * @param methodName the of the method for which the parameters should be retrieved * @return a map from Method object to the parameter names or null if the class was compiled without debug symbols on */ public Map> getAllMethodParameters(Class clazz, String methodName) { // Determine the constructors? Method[] methods = getMethods(clazz, methodName); if (methods.length == 0) { return Collections.emptyMap(); } // Check the cache if (methodCache.containsKey(methods[0])) { Map> methodParameters = new HashMap>(); for (Method method : methods) { methodParameters.put(method, methodCache.get(method)); } return methodParameters; } // Load the parameter names using ASM Map> methodParameters = new HashMap>(); try { ClassReader reader = AsmParameterNameLoader.createClassReader(clazz); AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor visitor = new AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor(clazz, methodName); reader.accept(visitor, 0); Map exceptions = visitor.getExceptions(); if (exceptions.size() == 1) { throw new RuntimeException((Exception) exceptions.values().iterator().next()); } if (!exceptions.isEmpty()) { throw new RuntimeException(exceptions.toString()); } methodParameters = visitor.getMethodParameters(); } catch (IOException ex) { } // Cache the names for (Method method : methods) { methodCache.put(method, methodParameters.get(method)); } return methodParameters; } private Method[] getMethods(Class clazz, String methodName) { List methods = new ArrayList(Arrays.asList(clazz.getMethods())); methods.addAll(Arrays.asList(clazz.getDeclaredMethods())); List matchingMethod = new ArrayList(methods.size()); for (Method method : methods) { if (method.getName().equals(methodName)) { matchingMethod.add(method); } } return matchingMethod.toArray(new Method[matchingMethod.size()]); } private static ClassReader createClassReader(Class declaringClass) throws IOException { InputStream in = null; try { ClassLoader classLoader = declaringClass.getClassLoader(); in = classLoader.getResourceAsStream(declaringClass.getName().replace('.', '/') + ".class"); ClassReader reader = new ClassReader(in); return reader; } finally { if (in != null) { try { in.close(); } catch (IOException ignored) { } } } } private static class AllParameterNamesDiscoveringVisitor extends EmptyVisitor { private final Map> constructorParameters = new HashMap>(); private final Map> methodParameters = new HashMap>(); private final Map exceptions = new HashMap(); private final String methodName; private final Map methodMap = new HashMap(); private final Map constructorMap = new HashMap(); public AllParameterNamesDiscoveringVisitor(Class type, String methodName) { this.methodName = methodName; List methods = new ArrayList(Arrays.asList(type.getMethods())); methods.addAll(Arrays.asList(type.getDeclaredMethods())); for (Method method : methods) { if (method.getName().equals(methodName)) { methodMap.put(Type.getMethodDescriptor(method), method); } } } public AllParameterNamesDiscoveringVisitor(Class type) { this.methodName = ""; List constructors = new ArrayList(Arrays.asList(type.getConstructors())); constructors.addAll(Arrays.asList(type.getDeclaredConstructors())); for (Constructor constructor : constructors) { Type[] types = new Type[constructor.getParameterTypes().length]; for (int j = 0; j < types.length; j++) { types[j] = Type.getType(constructor.getParameterTypes()[j]); } constructorMap.put(Type.getMethodDescriptor(Type.VOID_TYPE, types), constructor); } } public Map> getConstructorParameters() { return constructorParameters; } public Map> getMethodParameters() { return methodParameters; } public Map getExceptions() { return exceptions; } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (!name.equals(this.methodName)) { return null; } try { final List parameterNames; final boolean isStaticMethod; if (methodName.equals("")) { Constructor constructor = constructorMap.get(desc); if (constructor == null) { return null; } parameterNames = new ArrayList(constructor.getParameterTypes().length); parameterNames.addAll(Collections.nCopies(constructor.getParameterTypes().length, null)); constructorParameters.put(constructor, parameterNames); isStaticMethod = false; } else { Method method = methodMap.get(desc); if (method == null) { return null; } parameterNames = new ArrayList(method.getParameterTypes().length); parameterNames.addAll(Collections.nCopies(method.getParameterTypes().length, null)); methodParameters.put(method, parameterNames); isStaticMethod = Modifier.isStatic(method.getModifiers()); } return new EmptyVisitor() { // assume static method until we get a first parameter name public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) { if (isStaticMethod) { if (index < parameterNames.size()) { parameterNames.set(index, name); } } else if (index > 0) { // for non-static the 0th arg is "this" so we need to offset by -1 index--; if (index < parameterNames.size()) { parameterNames.set(index, name); } } } }; } catch (Exception e) { this.exceptions.put(signature, e); } return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy