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

classparse.ClassParse.scala Maven / Gradle / Ivy

The newest version!
package classparse

import fastparse.byte.all._
import BE._
import scala.collection.mutable.ArrayBuffer
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7

object ClassParse {

  object BasicElems {

    sealed abstract class BasicElem

    case class StringElem(string: String) extends BasicElem
    case class IntElem(int: Int) extends BasicElem
    case class FloatElem(float: Float) extends BasicElem
    case class LongElem(long: Long) extends BasicElem
    case class DoubleElem(double: Double) extends BasicElem
  }

  object Info {
    import BasicElems._

    sealed trait PoolInfo

    case class NopInfo() extends PoolInfo // it's used as a stub for Long and Double info
                                          // see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.5

    case class ClassInfo(nameIndex: Int) extends PoolInfo

    case class FieldRefInfo(classIndex: Int, nameAndTypeIndex: Int) extends PoolInfo
    case class MethodRefInfo(classIndex: Int, nameAndTypeIndex: Int) extends PoolInfo
    case class InterfaceMethodRefInfo(classIndex: Int, nameAndTypeIndex: Int) extends PoolInfo

    case class StringInfo(stringIndex: Int) extends PoolInfo

    case class BasicElemInfo(basicElem: BasicElem) extends PoolInfo

    case class NameAndTypeInfo(nameIndex: Int, descriptorIndex: Int) extends PoolInfo

    case class Utf8Info(bytes: Bytes) extends PoolInfo {
      lazy val getString = new String(bytes.toArray, "UTF-8")
    }

    case class MethodHandleInfo(referenceKind: Int, referenceIndex: Int) extends PoolInfo

    case class MethodTypeInfo(desctiptorIndex: Int) extends PoolInfo

    case class InvokeDynamicInfo(bootstrapMethodAttrIndex: Int, nameAndTypeIndex: Int) extends PoolInfo

    case class AttributeInfo(nameIndex: Int, info: Bytes)

    case class FieldInfo(accessFlags: Bytes, nameIndex: Int,
                         descriptorIndex: Int, attributes: Seq[AttributeInfo])

    case class MethodInfo(accessFlags: Bytes, nameIndex: Int,
                          descriptorIndex: Int, attributes: Seq[AttributeInfo])

    case class ClassFileInfo(minorVersion: Int, majorVersion: Int,
                             pool: Seq[PoolInfo], accessFlags: Bytes,
                             thisClassIndex: Int, superClassIndex: Int,
                             interfacesIndexes: Seq[Int], fields: Seq[FieldInfo],
                             methods: Seq[MethodInfo], attributes: Seq[AttributeInfo]) {

      def getInfoByIndex[T](idx: Int): Option[T] = {
        if (idx > pool.length || idx <= 0) {
          None
        } else {
          pool(idx - 1) match {
            case info: T => Some(info)
            case _ => None
          }
        }
      }

      def getStringByIndex(idx: Int) =
        getInfoByIndex[Utf8Info](idx).get.getString
    }
  }

  object Ast {
    import BasicElems._
    import Info._
    import ClassAttributes.Attribute

    sealed trait PoolItem

    case object Nop extends PoolItem // see NopeInfo

    case class Class(name: String) extends PoolItem

    object Class {
      def apply(classInfo: Info.ClassFileInfo, info: ClassInfo): Class =
        Class(classInfo.getStringByIndex(info.nameIndex))
    }

    case class NameAndType(name: String, descriptor: String) extends PoolItem

    object NameAndType {
      def apply(classInfo: Info.ClassFileInfo, info: NameAndTypeInfo): NameAndType =
        NameAndType(
          classInfo.getStringByIndex(info.nameIndex),
          classInfo.getStringByIndex(info.descriptorIndex)
        )
    }

    sealed trait Ref extends PoolItem
    case class FieldRef(name: String, descriptor: String, refClass: Class) extends Ref
    case class MethodRef(name: String, descriptor: String, refClass: Class) extends Ref
    case class InterfaceMethodRef(name: String, descriptor: String, refClass: Class) extends Ref

    object FieldRef {
      def apply(classInfo: Info.ClassFileInfo, info: FieldRefInfo): FieldRef = {
        val NameAndType(name, descriptor) = NameAndType(
            classInfo,
            classInfo.getInfoByIndex[NameAndTypeInfo](info.nameAndTypeIndex).get
          )

        FieldRef(
          name,
          descriptor,
          Class(classInfo, classInfo.getInfoByIndex[ClassInfo](info.classIndex).get)
        )
      }
    }

    object MethodRef {
      def apply(classInfo: Info.ClassFileInfo, info: MethodRefInfo): MethodRef = {
        val NameAndType(name, descriptor) = NameAndType(
          classInfo,
          classInfo.getInfoByIndex[NameAndTypeInfo](info.nameAndTypeIndex).get
        )

        MethodRef(
          name,
          descriptor,
          Class(classInfo, classInfo.getInfoByIndex[ClassInfo](info.classIndex).get)
        )
      }
    }

    object InterfaceMethodRef {
      def apply(classInfo: Info.ClassFileInfo, info: InterfaceMethodRefInfo): InterfaceMethodRef = {
        val NameAndType(name, descriptor) = NameAndType(
          classInfo,
          classInfo.getInfoByIndex[NameAndTypeInfo](info.nameAndTypeIndex).get
        )

        InterfaceMethodRef(
          name,
          descriptor,
          Class(classInfo, classInfo.getInfoByIndex[ClassInfo](info.classIndex).get)
        )
      }
    }

    object RefKind extends Enumeration {
      val getField, getStatic, putField, putStatic,
          invokeVirtual, invokeStatic, invokeSpecial, newInvokeSpecial,
          invokeInterface = Value
    }

    case class BasicElemPool(basicElem: BasicElem) extends PoolItem

    object BasicElemPool {
      def apply(classInfo: Info.ClassFileInfo, info: BasicElemInfo) =
        new BasicElemPool(info.basicElem)
    }

    case class MethodHandle(kind: RefKind.Value, ref: Ref) extends PoolItem

    object MethodHandle {
      def apply(classInfo: Info.ClassFileInfo, info: MethodHandleInfo) =
        new MethodHandle(
          RefKind(info.referenceKind - 1),
          classInfo.getInfoByIndex[PoolInfo](info.referenceIndex).get match {
            case fr: FieldRefInfo => FieldRef(classInfo, fr)
            case mr: MethodRefInfo => MethodRef(classInfo, mr)
            case imr: InterfaceMethodRefInfo => InterfaceMethodRef(classInfo, imr)
          }
        )
    }

    case class MethodType(descriptor: String) extends PoolItem

    object MethodType {
      def apply(classInfo: Info.ClassFileInfo, info: MethodTypeInfo) =
        new MethodType(classInfo.getStringByIndex(info.desctiptorIndex))
    }

    case class InvokeDynamic(name: String, descriptor: String, bootstrapIndex: Int) extends PoolItem

    object InvokeDynamic {
      def apply(classInfo: Info.ClassFileInfo, info: InvokeDynamicInfo): InvokeDynamic = {
        val NameAndType(name, descriptor) = NameAndType(
          classInfo,
          classInfo.getInfoByIndex[NameAndTypeInfo](info.nameAndTypeIndex).get
        )

        InvokeDynamic(name, descriptor, info.bootstrapMethodAttrIndex)
      }
    }


    private def getFlag(f: Byte, mask: Int) = (f & mask) != 0

    case class InnerClassFlags(accPublic: Boolean,   accPrivate: Boolean,   accProtected: Boolean,
                               accStatic: Boolean,   accFinal: Boolean,     accInterface: Boolean,
                               accAbstract: Boolean, accSynthetic: Boolean, accAnnotation: Boolean,
                               accEnum: Boolean){

      def this(fs: Bytes) = this(
        getFlag(fs(1), 0x01), getFlag(fs(1), 0x02), getFlag(fs(1), 0x04),
        getFlag(fs(1), 0x08), getFlag(fs(1), 0x10), getFlag(fs(0), 0x02),
        getFlag(fs(0), 0x04), getFlag(fs(0), 0x10), getFlag(fs(0), 0x20),
        getFlag(fs(0), 0x40))
    }


    object InnerClassFlags {
      def apply(fs: Bytes) = new InnerClassFlags(fs)
    }

    case class FieldFlags(accPublic: Boolean,    accPrivate: Boolean,   accProtected: Boolean,
                          accStatic: Boolean,    accFinal: Boolean,     accVolatile: Boolean,
                          accTransient: Boolean, accSynthetic: Boolean, accEnum: Boolean) {

      def this(fs: Bytes) = this(
        getFlag(fs(1), 0x01), getFlag(fs(1), 0x02), getFlag(fs(1), 0x04),
        getFlag(fs(1), 0x08), getFlag(fs(1), 0x10), getFlag(fs(1), 0x40),
        getFlag(fs(1), 0x80), getFlag(fs(0), 0x10), getFlag(fs(0), 0x40))
    }

    object FieldFlags {
      def apply(fs: Bytes) = new FieldFlags(fs)
    }

    case class MethodFlags(accPublic: Boolean,    accPrivate: Boolean,   accProtected: Boolean,
                           accStatic: Boolean,    accFinal: Boolean,     accSynchronized: Boolean,
                           accBridge: Boolean,    accVarargs: Boolean,   accNative: Boolean,
                           accAbstract: Boolean,  accStrict: Boolean,    accSynthetic: Boolean) {

      def this(fs: Bytes) = this(
        getFlag(fs(1), 0x01), getFlag(fs(1), 0x02), getFlag(fs(1), 0x04),
        getFlag(fs(1), 0x08), getFlag(fs(1), 0x10), getFlag(fs(1), 0x20),
        getFlag(fs(1), 0x40), getFlag(fs(1), 0x80), getFlag(fs(0), 0x01),
        getFlag(fs(0), 0x04), getFlag(fs(0), 0x08), getFlag(fs(0), 0x10))
    }

    object MethodFlags {
      def apply(fs: Bytes) = new MethodFlags(fs)
    }

    case class ClassFlags(accPublic: Boolean,     accFinal: Boolean,    accSuper: Boolean,
                          accInterface: Boolean,  accAbstract: Boolean, accSynthetic: Boolean,
                          accAnnotation: Boolean, accEnum: Boolean){

      def this(fs: Bytes) = this(
        getFlag(fs(1), 0x01), getFlag(fs(1), 0x10), getFlag(fs(1), 0x20),
        getFlag(fs(0), 0x02), getFlag(fs(0), 0x04), getFlag(fs(0), 0x10),
        getFlag(fs(0), 0x20), getFlag(fs(0), 0x40))
    }

    object ClassFlags {
      def apply(fs: Bytes) = new ClassFlags(fs)
    }

    case class Field(name: String, descriptor: String, flags: FieldFlags, attributes: Seq[Attribute])

    case class Method(name: String, descriptor: String, flags: MethodFlags, attributes: Seq[Attribute])

    case class ClassFile(minorVersion: Int,       majorVersion: Int,
                         accessFlags: ClassFlags, pool: Seq[PoolItem],
                         thisClass: Class,        superClass: Option[Class],
                         interfaces: Seq[Class],  fields: Seq[Field],
                         methods: Seq[Method],    attributes: Seq[Attribute])

    def convertToPoolItem(classInfo: Info.ClassFileInfo, info: PoolInfo): PoolItem = {
      info match {
        case info: NopInfo => Nop
        case info: ClassInfo => Class(classInfo, info)
        case info: FieldRefInfo => FieldRef(classInfo, info)
        case info: MethodRefInfo => MethodRef(classInfo, info)
        case info: InterfaceMethodRefInfo => InterfaceMethodRef(classInfo, info)
        case info: MethodTypeInfo => MethodType(classInfo, info)
        case info: NameAndTypeInfo => NameAndType(classInfo, info)
        case StringInfo(strIdx) => BasicElemPool(StringElem(classInfo.getStringByIndex(strIdx)))
        case info: BasicElemInfo => BasicElemPool(classInfo, info)
        case utf8info: Utf8Info => BasicElemPool(StringElem(utf8info.getString))
        case info: MethodHandleInfo => MethodHandle(classInfo, info)
        case info: InvokeDynamicInfo => InvokeDynamic(classInfo, info)
      }
    }

    def convertToAst(classInfo: Info.ClassFileInfo): ClassFile = {
      import ClassAttributes.convertToAttribute

      def getFieldFromInfo(fieldInfo: FieldInfo) = Field(
        classInfo.getStringByIndex(fieldInfo.nameIndex),
        classInfo.getStringByIndex(fieldInfo.descriptorIndex),
        FieldFlags(fieldInfo.accessFlags),
        fieldInfo.attributes.map(attr => convertToAttribute(classInfo, attr))
      )

      def getMethodFromInfo(methodInfo: MethodInfo) = Method(
        classInfo.getStringByIndex(methodInfo.nameIndex),
        classInfo.getStringByIndex(methodInfo.descriptorIndex),
        MethodFlags(methodInfo.accessFlags),
        methodInfo.attributes.map(attr => convertToAttribute(classInfo, attr))
      )

      ClassFile(
        classInfo.minorVersion,
        classInfo.majorVersion,
        ClassFlags(classInfo.accessFlags),
        classInfo.pool.map(info => convertToPoolItem(classInfo, info)),
        Class(classInfo, classInfo.getInfoByIndex[ClassInfo](classInfo.thisClassIndex).get),
        if (classInfo.superClassIndex == 0)
          None
        else
          Some(Class(classInfo, classInfo.getInfoByIndex[ClassInfo](classInfo.superClassIndex).get)),
        classInfo.interfacesIndexes.map(idx =>
          Class(classInfo, classInfo.getInfoByIndex[ClassInfo](idx).get)),
        classInfo.fields.map(getFieldFromInfo),
        classInfo.methods.map(getMethodFromInfo),
        classInfo.attributes.map(attr => convertToAttribute(classInfo, attr))
      )

    }
  }

  import Info._
  import BasicElems._


  val constantClassInfo = {
    val name_index = UInt16
    P( BS(7) ~/ name_index ).map(ClassInfo)
  }

  val constantFieldRefInfo = {
    val class_index = UInt16
    val name_and_type_index = UInt16
    P( BS(9) ~/ class_index ~ name_and_type_index ).map(FieldRefInfo.tupled)
  }

  val constantMethodRefInfo = {
    val class_index = UInt16
    val name_and_type_index = UInt16
    P( BS(10) ~/ class_index ~ name_and_type_index ).map(MethodRefInfo.tupled)
  }

  val constantInterfaceMethodRefInfo = {
    val class_index = UInt16
    val name_and_type_index = UInt16
    P( BS(11) ~/ class_index ~ name_and_type_index ).map(InterfaceMethodRefInfo.tupled)
  }

  val constantStringInfo = {
    val string_index = UInt16
    P( BS(8) ~/ string_index ).map(StringInfo)
  }

  val constantIntInfo = P( BS(3) ~/ Int32 ).map(i =>
    BasicElemInfo(IntElem(i))
  )
  val constantFloatInfo = P( BS(4) ~/ Float32 ).map(f =>
    BasicElemInfo(FloatElem(f))
  )
  val constantLongInfo = P( BS(5) ~/ Int64 ).map(i =>
    BasicElemInfo(LongElem(i))
  )
  val constantDoubleInfo = P( BS(6) ~/ Float64 ).map(f =>
    BasicElemInfo(DoubleElem(f))
  )

  val constantNameAndTypeInfo = {
    val name_index = UInt16
    val descriptor_index = UInt16
    P( BS(12) ~/ name_index ~ descriptor_index ).map(NameAndTypeInfo.tupled)
  }

  val constantUtf8Info =
    P( BS(1) ~/ UInt16.flatMap(l => AnyBytes(l).!) ).map(Utf8Info)

  val constantMethodHandleInfo = {
    val reference_kind = AnyByte.!
    val reference_index = UInt16
    P( BS(15) ~/ reference_kind ~ reference_index ).map {
      case (refKind, refIdx) => MethodHandleInfo(refKind(0).toInt, refIdx)
    }
  }

  val constantMethodTypeInfo = {
    val descriptor_index = UInt16
    P( BS(16) ~/ descriptor_index ).map(MethodTypeInfo)
  }

  val constantInvokeDynamicInfo = {
    val bootstrap_method_attr_index = UInt16
    val name_and_type_index = UInt16
    P( BS(18) ~/ bootstrap_method_attr_index ~ name_and_type_index ).map(InvokeDynamicInfo.tupled)
  }

  val singleConstantPoolItem = P( constantClassInfo      | constantFieldRefInfo |
                                  constantMethodRefInfo  | constantInterfaceMethodRefInfo |
                                  constantStringInfo     | constantIntInfo |
                                  constantFloatInfo      | constantNameAndTypeInfo |
                                  constantUtf8Info       | constantMethodHandleInfo |
                                  constantMethodTypeInfo | constantInvokeDynamicInfo )

  val doubleConstantPoolItem = P( constantDoubleInfo | constantLongInfo)

  def constantPool(count: Int): P[Seq[PoolInfo]] =
    if (count == 0) {
      Pass.map(_ => ArrayBuffer[PoolInfo]())
    } else {
        P( singleConstantPoolItem.~/.flatMap(item => constantPool(count - 1).map(_ :+ item)) |
           doubleConstantPoolItem.~/.flatMap(item => constantPool(count - 2).map(_ :+ NopInfo() :+ item )) )
      //TODO it's pretty slow solution, as I think, but it's only one that I know currently
    }

  val attributeInfo = {
    val attribute_name_index = UInt16
    val attributes = Int32.flatMap(l => AnyByte.rep(exactly = l).!)

    P( attribute_name_index ~ attributes ).map(AttributeInfo.tupled)
  }


  val fieldInfo = {
    val access_flags = Word16.!
    val name_index = UInt16
    val descriptor_index = UInt16
    val attributes = repeatWithSize(UInt16, attributeInfo.~/)

    P( access_flags ~ name_index ~ descriptor_index ~ attributes ).map(FieldInfo.tupled)
  }

  val methodInfo = {
    val access_flags = Word16.!
    val name_index = UInt16
    val descriptor_index = UInt16
    val attributes = repeatWithSize(UInt16, attributeInfo.~/)

    P(access_flags ~ name_index ~ descriptor_index ~ attributes).map(MethodInfo.tupled)
  }

  val classFile = {
    val minor_version = UInt16
    val major_version = UInt16
    val constant_pool = UInt16.flatMap(l => constantPool(l - 1).map(_.reverse))
    val access_flags = Word16.!
    val this_class = UInt16
    val super_class = UInt16
    val interfaces = repeatWithSize(UInt16, UInt16.~/)
    val fields = repeatWithSize(UInt16, fieldInfo.~/)
    val methods = repeatWithSize(UInt16, methodInfo.~/)
    val attributes = repeatWithSize(UInt16, attributeInfo.~/)

    P(
      BS(0xCA, 0xFE, 0xBA, 0xBE) ~/
      minor_version ~ major_version ~ constant_pool ~
      access_flags ~  this_class ~    super_class ~
      interfaces ~    fields ~        methods ~       attributes
    ).map(ClassFileInfo.tupled)
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy