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

org.clapper.classutil.asm.ClassFinderImpl.scala Maven / Gradle / Ivy

The newest version!
/** The ASM-specific implementation of the Class Finder capabilities.
  */
package org.clapper.classutil.asm

import org.clapper.classutil._

import scala.collection.mutable.{Set => MutableSet, ArrayBuilder}

import org.objectweb.asm._

import java.io.{File, InputStream}

@SuppressWarnings(Array("org.wartremover.warts.MutableDataStructures"))
private[classutil] object ASMBitmapMapper {
  import java.lang.reflect.{Modifier => JModifier}

  val AccessMap = Map(
    Opcodes.ACC_ABSTRACT     -> Modifier.Abstract,
    Opcodes.ACC_FINAL        -> Modifier.Final,
    Opcodes.ACC_INTERFACE    -> Modifier.Interface,
    Opcodes.ACC_NATIVE       -> Modifier.Native,
    Opcodes.ACC_PRIVATE      -> Modifier.Private,
    Opcodes.ACC_PROTECTED    -> Modifier.Protected,
    Opcodes.ACC_PUBLIC       -> Modifier.Public,
    Opcodes.ACC_STATIC       -> Modifier.Static,
    Opcodes.ACC_STRICT       -> Modifier.Strict,
    Opcodes.ACC_SYNCHRONIZED -> Modifier.Synchronized,
    Opcodes.ACC_SYNTHETIC    -> Modifier.Synthetic,
    Opcodes.ACC_TRANSIENT    -> Modifier.Transient,
    Opcodes.ACC_VOLATILE     -> Modifier.Volatile
  )
}

@SuppressWarnings(Array("org.wartremover.warts.MutableDataStructures"))
private[classutil] trait ASMBitmapMapper {
  def mapModifiers(bitmap: Int, map: Map[Int, Modifier.Modifier]):
  Set[Modifier.Modifier] = {
    // Map the class's modifiers integer bitmap into a set of Modifier
    // enumeration values by filtering and keeping only the ones that
    // match the masks, extracting the corresponding map value, and
    // converting the whole thing to a set.
    //
    // Mutable collections are used for speed.
    val result = MutableSet[Modifier.Modifier]()
    for(pair <- map) {
      if((pair._1 & bitmap) != 0)
        result += pair._2
    }
    result.toSet
  }
}

@SuppressWarnings(Array("org.wartremover.warts.MutableDataStructures",
                        "org.wartremover.warts.Var"))
private[classutil] class ClassInfoImpl(val name: String,
                                       val superClassName: String,
                                       val interfaces: List[String],
                                       val signature: String,
                                       val access: Int,
                                       val location: File)
extends ClassInfo with ASMBitmapMapper {
  def methods     = Set.empty[MethodInfo] ++ methodSet
  def fields      = Set.empty[FieldInfo] ++ fieldSet
  def annotations = Set.empty[AnnotationInfo] ++ annotationSet

  var methodSet = MutableSet.empty[MethodInfo]
  var fieldSet = MutableSet.empty[FieldInfo]
  var annotationSet = MutableSet.empty[AnnotationInfo]
  val modifiers = mapModifiers(access, ASMBitmapMapper.AccessMap)
}

private[classutil] class MethodInfoImpl(val name: String,
                                        val signature: String,
                                        val descriptor: String,
                                        val exceptions: List[String],
                                        val access: Int)
extends MethodInfo with ASMBitmapMapper {
  val modifiers = mapModifiers(access, ASMBitmapMapper.AccessMap)
}

private[classutil] class FieldInfoImpl(val name: String,
                                       val signature: String,
                                       val descriptor: String,
                                       val value: Option[java.lang.Object],
                                       val access: Int)
extends FieldInfo with ASMBitmapMapper {
  val modifiers = mapModifiers(access, ASMBitmapMapper.AccessMap)
}

@SuppressWarnings(Array("org.wartremover.warts.Var"))
private[classutil] class AnnotationInfoImpl(val descriptor: String,
                                            val visible: Boolean)
extends AnnotationInfo {
  def params = Map.empty[String, Any] ++ paramMap

  var paramMap = Map.empty[String, Any]
}

@SuppressWarnings(Array("org.wartremover.warts.MutableDataStructures",
                        "org.wartremover.warts.Var"))
private[classutil] class ClassVisitor(location: File, apiVersion: Int)
extends ASMEmptyVisitor(apiVersion) with ASMBitmapMapper {

  var classes = MutableSet.empty[ClassInfo]
  var currentClass: Option[ClassInfoImpl] = None

  override def visit(version: Int,
                     access: Int,
                     name: String,
                     signature: String,
                     superName: String,
                     interfaces: Array[String]): Unit = {
    val sig = if (signature != null) signature else ""
    val classInfo = new ClassInfoImpl(mapClassName(name),
                                      mapClassName(superName),
                                      interfaces.toList.map(mapClassName(_)),
                                      sig,
                                      access,
                                      location)
    classes += classInfo
    currentClass = Some(classInfo)
  }

  @SuppressWarnings(Array("org.wartremover.warts.Null"))
  override def visitMethod(access: Int,
                           name: String,
                           descriptor: String,
                           signature: String,
                           exceptions: Array[String]): MethodVisitor = {
    assert(currentClass.isDefined)
    val sig = if (signature != null) signature else ""
    val excList = Option(exceptions).map(_.toList).getOrElse(Nil)
    currentClass.foreach { c =>
      c.methodSet += new MethodInfoImpl(name, sig, descriptor, excList, access)
    }

    null
  }

  @SuppressWarnings(Array("org.wartremover.warts.Null"))
  override def visitField(access: Int,
                          name: String,
                          descriptor: String,
                          signature: String,
                          value: java.lang.Object): FieldVisitor = {
    assert(currentClass.isDefined)
    val sig = if (signature != null) signature else ""
    val initialVal = Option(value)
    currentClass.foreach { c =>
      c.fieldSet += new FieldInfoImpl(name, sig, descriptor, initialVal, access)
    }

    null
  }

  class AnnotationVisitor(annotationInfo: AnnotationInfoImpl)
    extends org.objectweb.asm.AnnotationVisitor(api) {

    override def visit(name: String, value: Any): Unit =
      annotationInfo.paramMap += (name -> value)

    override def visitEnum(name: String, desc: String, value: String): Unit =
      annotationInfo.paramMap += (name -> value)

    override def visitAnnotation(name: String, desc: String): AnnotationVisitor = {
      val innerAnnInfo = new AnnotationInfoImpl(desc, annotationInfo.visible)
      annotationInfo.paramMap += (name -> innerAnnInfo)
      new AnnotationVisitor(innerAnnInfo)
    }

    override def visitArray(name: String): AnnotationArrayVisitor = {
      new AnnotationArrayVisitor {
        override def visitEnd(): Unit = {
          annotationInfo.paramMap += (name -> arrBuilder.result)
        }
      }
    }
  }

  class AnnotationArrayVisitor extends org.objectweb.asm.AnnotationVisitor(api) {
    protected val arrBuilder: ArrayBuilder[Any] = ArrayBuilder.make[Any]

    override def visit(name: String, value: Any): Unit = arrBuilder += value

    override def visitEnum(name: String, desc: String, value: String): Unit = {
      arrBuilder += value
    }

    override def visitAnnotation(name: String, desc: String) = {
      val innerAnnInfo = new AnnotationInfoImpl(desc, false)
      arrBuilder += innerAnnInfo
      new AnnotationVisitor(innerAnnInfo)
    }

    override def visitArray(name: String) = {
      val outer = this
      new AnnotationArrayVisitor {
        override def visitEnd(): Unit = outer.arrBuilder += arrBuilder.result
      }
    }
  }

  override def visitAnnotation(descriptor: String,
                               visible: Boolean): AnnotationVisitor = {
    assert(currentClass.isDefined)
    val annotationInfo = new AnnotationInfoImpl(descriptor, visible)
    currentClass.foreach { c => c.annotationSet += annotationInfo }

    new AnnotationVisitor(annotationInfo)
  }

  private def mapClassName(name: String): String = {
    if (name == null) ""
    else name.replaceAll("/", ".")
  }
}

private[classutil] object ClassFile {
  val ASMAcceptCriteria = 0

  def load(is: InputStream, location: File, asmVersion: Int): Iterator[ClassInfo] = {
    val cr = new ClassReader(is)
    val visitor = new ClassVisitor(location, asmVersion)
    cr.accept(visitor, ASMAcceptCriteria)
    visitor.classes.iterator
  }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy