基于ASM实现方法参数名的收集

open class LocalVariableTableParameterNameDiscoverer : ParameterNameDiscoverer {  
    companion object {  
        private val NO_DEBUG_INF_MAP = emptyMap<Executable, Array<String>>()  
    }  

    private val parameterNamesCache: MutableMap<Class<*>, Map<Executable, Array<String>>> = ConcurrentHashMap()  

    override fun getParameterNames(method: Method): Array<String>? {  
        return doGetParameterNames(method)  
    }  

    override fun getParameterNames(constructor: Constructor<*>): Array<String>? {  
        return doGetParameterNames(constructor)  
    }  

    private fun doGetParameterNames(executable: Executable): Array<String>? {  
        val declaringClass = executable.declaringClass  
  val map = parameterNamesCache.computeIfAbsent(declaringClass, this::inspectClass)  
        return if (map == NO_DEBUG_INF_MAP) null else map[executable]  
    }  

    private fun inspectClass(clazz: Class<*>): Map<Executable, Array<String>> {  
        val classFile = clazz.name.substring(clazz.name.lastIndexOf(".") + 1) + ".class"  
  val stream = clazz.getResourceAsStream(classFile)  
        if (stream == null) {  
            // log...  
  return NO_DEBUG_INF_MAP  
  }  

        try {  
            val classReader = ClassReader(stream)  
            val paramNameMap = ConcurrentHashMap<Executable, Array<String>>()  
            classReader.accept(ParameterNameDiscoveringVisitor(clazz, paramNameMap), 0)  
            return paramNameMap  
        } catch (ex: IOException) {  
            // ignored  
  } catch (ex: IllegalArgumentException) {  
            // ignored  
  } finally {  
            try {  
                stream.close()  
            } catch (ignored: IOException) {  
                // ignored  
  }  
        }  
        return NO_DEBUG_INF_MAP  
  }  

    /**  
 * 进行参数名的获取的[ClassVisitor]  
  *  
 * @param map 收集参数名的map(Key-方法, Value-该方法的参数名列表)  
 */  private class ParameterNameDiscoveringVisitor(  
        private val clazz: Class<*>,  
        private val map: MutableMap<Executable, Array<String>>  
    ) : ClassVisitor(SpringAsmInfo.ASM_VERSION) {  

        /**  
 * 访问类上的一个方法, 对方法当中的数据进行采集  
  *  
 * @param access accessFlag  
 * @param name 方法名称, 包含构造器和静态初始化代码块方法 , eg: "&lt;init&gt;", "&lt;clinit&gt;", "foo" * @param descriptor 方法签名 eg: "(Ljava/lang/String;)V", "()V" * @param exceptions exceptionTables  
 */  override fun visitMethod(  
            access: Int,  
            name: String,  
            descriptor: String,  
            signature: String?,  
            exceptions: Array<out String>?  
        ): MethodVisitor? {  
            // constructor => name=<init>  
 // static init block => name=<clinit>  if (name == "<clinit>") {  
                return null  
  }  
            return LocalVariableTableVisitor(clazz, map, name, descriptor, isStatic(access))  
        }  

        private fun isStatic(access: Int): Boolean = (Opcodes.ACC_STATIC and access) != 0  
  }  

    /**  
 * 访问方法的局部变量表的方式进行参数名的收集  
  *  
 * @param clazz 访问的类  
  * @param map 采集结果的Map, Key是方法, Value是该方法的参数名列表  
  * @param name 方法名称  
  * @param desc 方法描述符  
  */  
  private class LocalVariableTableVisitor(  
        private val clazz: Class<*>,  
        private val map: MutableMap<Executable, Array<String>>,  
        private val name: String,  
        desc: String?,  
        private val staticMethod: Boolean,  
    ) : MethodVisitor(SpringAsmInfo.ASM_VERSION) {  

        /**  
 * 根据方法描述符desc可以获取到参数类型列表  
  */  
  private val args = Type.getArgumentTypes(desc)  

        /**  
 * 计算局部变量表当中每个参数所在的槽位slotIndex, index=参数索引, value=该参数在局部变量表当中的第几个slot  
 */  private val lvtSlotIndex: IntArray = computeLocalVariableTableIndex()  

        /**  
 * 参数名列表, index=参数索引, value=该参数的名字  
  */  
  private val parameterNames = Array<String?>(args.size) { null }  

  /**  
 * 是否存在有局部变量表的信息, 如果访问到了局部变量表, 那么设置为true  
 */  private var hasLocalVariableInfo = false  

  /**  
 * 访问其中一个局部变量  
  *  
 * @param name 局部变量表的name, 如果是实例方法, 第一个参数为"this"  
 * @param descriptor 参数类型的描述信息, 比如"[I", "Ljava/lang/String;"  
 * @param index 当前局部变量slot的位置index  
 */  override fun visitLocalVariable(  
            name: String,  
            descriptor: String,  
            signature: String?,  
            start: Label?,  
            end: Label?,  
            index: Int  
        ) {  
            this.hasLocalVariableInfo = true  
 for (i in lvtSlotIndex.indices) {  
                if (lvtSlotIndex[i] == index) {  
                    parameterNames[i] = name  
                }  
            }  
        }  

        @Suppress("UNCHECKED_CAST")  
        override fun visitEnd() {  
            if (hasLocalVariableInfo || (staticMethod && parameterNames.isEmpty())) {  
                val executable = resolveExecutable()  
                map[executable] = parameterNames as Array<String>  
            }  
        }  

        private fun resolveExecutable(): Executable {  
            val argumentTypes = args.map { ClassUtils.resolveClassName(it.className, clazz.classLoader) }.toTypedArray()  
            if (name == "<init>") {  
                return clazz.getDeclaredConstructor(*argumentTypes)  
            } else {  
                return clazz.getDeclaredMethod(name, *argumentTypes)  
            }  
        }  

        private fun computeLocalVariableTableIndex(): IntArray {  
            var nextParamIndex = if (staticMethod) 0 else 1  
  return this.args.indices.map {  
  val currentIndex = nextParamIndex  
                nextParamIndex = if (it == Type.LONG || it == Type.DOUBLE) nextParamIndex + 2 else nextParamIndex + 1  
  return@map currentIndex  
            }.toIntArray()  
        }  
    }  
}