summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
blob: 402dc66a7f39a21781999c7b3b029b7b94a5e12c (plain) (tree)
1
2
3
4
5
6
7






                                   





                                                                                                 
                                        

                                         
                                                           
                                                                   
















                                                                                             
                                                                                               








                                                                                              
                           



                                                
                           

   



                                                                         
                                                                                          

        
 








                                                                              
                                                       


                                                                                         














                                                                                                                                                    
 

                     

   






                                                                                              

   









                                                                        
   















                                                                                 









                                                            

















                                                                               




                                                                                             
                                                                                             



                                                      
                                                                                   


                                        
 
/* NSC -- new Scala compiler
 * Copyright 2005-2014 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.tools.nsc.backend.jvm

import scala.tools.asm.tree.{AbstractInsnNode, ClassNode, FieldNode, InsnList, MethodNode}
import java.io.{PrintWriter, StringWriter}
import java.util

import scala.tools.asm.util.{CheckClassAdapter, Textifier, TraceClassVisitor, TraceMethodVisitor}
import scala.tools.asm.{Attribute, ClassReader, ClassWriter}
import scala.collection.JavaConverters._
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.tools.nsc.backend.jvm.analysis.InitialProducer
import scala.tools.nsc.backend.jvm.opt.InlineInfoAttributePrototype

object AsmUtils {

  /**
   * Print the bytecode of methods generated by GenBCode to the standard output. Only methods
   * whose name contains `traceMethodPattern` are traced.
   */
  final val traceMethodEnabled = false
  final val traceMethodPattern = ""

  /**
   * Print the bytecode of classes generated by GenBCode to the standard output.
   */
  final val traceClassEnabled = false
  final val traceClassPattern = ""

  /**
   * Print the bytecode of classes as they are serialized by the ASM library. The serialization
   * performed by `asm.ClassWriter` can change the code generated by GenBCode. For example, it
   * introduces stack map frames, it computes the maximal stack sizes, and it replaces dead
   * code by NOPs (see also https://github.com/scala/scala/pull/3726#issuecomment-42861780).
   */
  final val traceSerializedClassEnabled = false
  final val traceSerializedClassPattern = ""

  def traceMethod(mnode: MethodNode): Unit = {
    println(s"Bytecode for method ${mnode.name}")
    println(textify(mnode))
  }

  def traceClass(cnode: ClassNode): Unit = {
    println(s"Bytecode for class ${cnode.name}")
    println(textify(cnode))
  }

  def traceClass(bytes: Array[Byte]): Unit = traceClass(readClass(bytes))

  def readClass(bytes: Array[Byte]): ClassNode = {
    val node = new ClassNode()
    new ClassReader(bytes).accept(node, Array[Attribute](InlineInfoAttributePrototype), 0)
    node
  }

  def readClass(filename: String): ClassNode = readClass(classBytes(filename))

  def classBytes(file: String): Array[Byte] = {
    val f = new java.io.RandomAccessFile(file, "r")
    val bytes = new Array[Byte](f.length.toInt)
    f.read(bytes)
    bytes
  }

  def classFromBytes(bytes: Array[Byte]): ClassNode = {
    val node = new ClassNode()
    new ClassReader(bytes).accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES)

    node
  }

//  def main(args: Array[String]): Unit = println(textify(sortedClassRead(classBytes(args.head))))

  def sortClassMembers(node: ClassNode): node.type = {
    node.fields.sort(_.name compareTo _.name)
    node.methods.sort(_.name compareTo _.name)
    node
  }

  // drop ScalaSig annotation and class attributes
  def zapScalaClassAttrs(node: ClassNode): node.type = {
    if (node.visibleAnnotations != null)
      node.visibleAnnotations = node.visibleAnnotations.asScala.filterNot(a => a == null || a.desc.contains("Lscala/reflect/ScalaSignature")).asJava

    node.attrs = null
    node
  }

  def main(args: Array[String]): Unit = args.par.foreach { classFileName =>
    val node = zapScalaClassAttrs(sortClassMembers(classFromBytes(classBytes(classFileName))))

    val pw = new PrintWriter(classFileName + ".asm")
    val trace = new TraceClassVisitor(pw)
    node.accept(trace)
    pw.close()
  }

  /**
   * Returns a human-readable representation of the cnode ClassNode.
   */
  def textify(cnode: ClassNode): String = {
    val trace = new TraceClassVisitor(new PrintWriter(new StringWriter))
    cnode.accept(trace)
    val sw = new StringWriter
    val pw = new PrintWriter(sw)
    trace.p.print(pw)
    sw.toString
  }

  /**
   * Returns a human-readable representation of the code in the mnode MethodNode.
   */
  def textify(mnode: MethodNode): String = {
    val trace = new TraceClassVisitor(new PrintWriter(new StringWriter))
    mnode.accept(trace)
    val sw = new StringWriter
    val pw = new PrintWriter(sw)
    trace.p.print(pw)
    sw.toString
  }

  /**
   * Returns a human-readable representation of the given instruction.
   */
  def textify(insn: AbstractInsnNode): String = insn match {
    case _: InitialProducer =>
      insn.toString
    case _ =>
      val trace = new TraceMethodVisitor(new Textifier)
      insn.accept(trace)
      val sw = new StringWriter
      val pw = new PrintWriter(sw)
      trace.p.print(pw)
      sw.toString.trim
  }

  /**
   * Returns a human-readable representation of the given instruction sequence.
   */
  def textify(insns: Iterator[AbstractInsnNode]): String = {
    val trace = new TraceMethodVisitor(new Textifier)
    insns.foreach(_.accept(trace))
    val sw: StringWriter = new StringWriter
    val pw: PrintWriter = new PrintWriter(sw)
    trace.p.print(pw)
    sw.toString.trim
  }

  /**
   * Returns a human-readable representation of the given instruction sequence.
   */
  def textify(insns: InsnList): String = textify(insns.iterator().asScala)

  /**
   * Run ASM's CheckClassAdapter over a class. Returns None if no problem is found, otherwise
   * Some(msg) with the verifier's error message.
   */
  def checkClass(classNode: ClassNode, dumpNonErroneous: Boolean = false): Option[String] = {
    val cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
    classNode.accept(cw)
    val sw = new StringWriter()
    val pw = new PrintWriter(sw)
    CheckClassAdapter.verify(new ClassReader(cw.toByteArray), dumpNonErroneous, pw)
    val res = sw.toString
    if (res.isEmpty) None else Some(res)
  }
}