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

scala.build.postprocessing.AsmPositionUpdater.scala Maven / Gradle / Ivy

package scala.build.postprocessing

import org.objectweb.asm

import scala.build.{Logger, Os}

object AsmPositionUpdater {

  private class LineNumberTableMethodVisitor(
    lineShift: Int,
    delegate: asm.MethodVisitor
  ) extends asm.MethodVisitor(asm.Opcodes.ASM9, delegate) {
    override def visitLineNumber(line: Int, start: asm.Label): Unit =
      super.visitLineNumber(line + lineShift, start)
  }

  private class LineNumberTableClassVisitor(
    mappings: Map[String, (String, Int)],
    cw: asm.ClassWriter
  ) extends asm.ClassVisitor(asm.Opcodes.ASM9, cw) {
    private var lineShiftOpt = Option.empty[Int]
    def mappedStuff          = lineShiftOpt.nonEmpty
    override def visitSource(source: String, debug: String): Unit =
      mappings.get(source) match {
        case None =>
          super.visitSource(source, debug)
        case Some((newSource, lineShift)) =>
          lineShiftOpt = Some(lineShift)
          super.visitSource(newSource, debug)
      }
    override def visitMethod(
      access: Int,
      name: String,
      descriptor: String,
      signature: String,
      exceptions: Array[String]
    ): asm.MethodVisitor = {
      val main = super.visitMethod(access, name, descriptor, signature, exceptions)
      lineShiftOpt match {
        case None            => main
        case Some(lineShift) => new LineNumberTableMethodVisitor(lineShift, main)
      }
    }
  }

  def postProcess(
    mappings: Map[String, (String, Int)],
    output: os.Path,
    logger: Logger
  ): Unit = {
    os.walk(output)
      .iterator
      .filter(os.isFile(_))
      .filter(_.last.endsWith(".class"))
      .foreach { path =>
        val is = os.read.inputStream(path)
        val updateByteCodeOpt =
          try {
            val reader  = new asm.ClassReader(is)
            val writer  = new asm.ClassWriter(reader, 0)
            val checker = new LineNumberTableClassVisitor(mappings, writer)
            reader.accept(checker, 0)
            if (checker.mappedStuff) Some(writer.toByteArray)
            else None
          }
          finally is.close()
        for (b <- updateByteCodeOpt) {
          logger.debug(s"Overwriting ${path.relativeTo(Os.pwd)}")
          os.write.over(path, b)
        }
      }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy