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

com.outr.jefe.optimize.ApplicationInspector.scala Maven / Gradle / Ivy

package com.outr.jefe.optimize

import java.util.jar.JarFile

import scribe.Logging
import org.objectweb.asm._

import scala.annotation.tailrec
import scala.collection.mutable

class ApplicationInspector(jar: JarFile) extends Logging {
  var referenced = Set.empty[String]
  private val backlog = mutable.Queue.empty[String]

  private val exclusions = Set("java/", "javax/", "org/xml/sax/", "sun/")

  val mv = new MethodVisitor(Opcodes.ASM5) {
    override def visitFieldInsn(opcode: Int, owner: String, name: String, desc: String): Unit = {
      enqueue(owner)
    }

    override def visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean): Unit = {
      enqueue(owner)
    }
  }
  private val cv = new ClassVisitor(Opcodes.ASM5) {
    override def visitField(access: Int, name: String, desc: String, signature: String, value: scala.Any): FieldVisitor = {
      if (desc.startsWith("L")) {
        val className = desc.substring(1, desc.length - 1)
        enqueue(className)
      }
      super.visitField(access, name, desc, signature, value)
    }

    override def visitMethod(access: Int, name: String, desc: String, signature: String, exceptions: Array[String]): MethodVisitor = mv
  }

  def process(className: String): Unit = {
    backlog += className
    processRecursively()
  }

  private def enqueue(className: String): Unit = {
    val exclude = exclusions.exists(exc => className.startsWith(exc))
    if (className.startsWith("[L") && className.endsWith(";")) {
      enqueue(className.substring(2, className.length - 1))
    } else if (className.indexOf('/') > -1 && !exclude && !referenced.contains(className)) {
      backlog += className
      referenced += className
    } else {
      logger.debug(s"Excluding: $className")
    }
  }

  @tailrec
  private def processRecursively(): Unit = {
    val className = backlog.dequeue()
    logger.info(s"Processing $className (referenced: ${referenced.size}, backlog: ${backlog.size})...")
    referenced += className

    val entry = jar.getEntry(s"$className.class")
    val input = jar.getInputStream(entry)
    try {
      val classReader = new ClassReader(input)
      classReader.accept(cv, 0)
    } finally {
      input.close()
    }

    if (backlog.nonEmpty) {
      processRecursively()
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy