From de7ac75196758361c5e4f0c923a62c8bd0ef8ceb Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Mon, 10 Jun 2024 16:16:57 +0200 Subject: [PATCH] Use jdi directly --- project/plugins.sbt | 1 + .../scala/decoder/jdi/JavaReflection.scala | 20 ------- .../ch/epfl/scala/decoder/jdi/JdiClass.scala | 43 +++++++++++++++ .../scala/decoder/jdi/JdiClassLoader.scala | 15 ++--- .../scala/decoder/jdi/JdiLocalVariable.scala | 6 +- .../epfl/scala/decoder/jdi/JdiLocation.scala | 4 -- .../ch/epfl/scala/decoder/jdi/JdiMethod.scala | 33 +++++------ .../scala/decoder/jdi/JdiReferenceType.scala | 55 ------------------- .../ch/epfl/scala/decoder/jdi/JdiType.scala | 4 +- 9 files changed, 68 insertions(+), 113 deletions(-) delete mode 100644 src/main/scala/ch/epfl/scala/decoder/jdi/JavaReflection.scala create mode 100644 src/main/scala/ch/epfl/scala/decoder/jdi/JdiClass.scala delete mode 100644 src/main/scala/ch/epfl/scala/decoder/jdi/JdiLocation.scala delete mode 100644 src/main/scala/ch/epfl/scala/decoder/jdi/JdiReferenceType.scala diff --git a/project/plugins.sbt b/project/plugins.sbt index 82cb262..3eb12e7 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1 +1,2 @@ addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") +addSbtPlugin("org.scala-debugger" % "sbt-jdi-tools" % "1.1.1") diff --git a/src/main/scala/ch/epfl/scala/decoder/jdi/JavaReflection.scala b/src/main/scala/ch/epfl/scala/decoder/jdi/JavaReflection.scala deleted file mode 100644 index 3fcfd51..0000000 --- a/src/main/scala/ch/epfl/scala/decoder/jdi/JavaReflection.scala +++ /dev/null @@ -1,20 +0,0 @@ -package ch.epfl.scala.decoder.jdi - -import scala.reflect.ClassTag -import java.lang.reflect.InvocationTargetException - -private[jdi] trait JavaReflection(obj: Any, className: String): - private val classLoader = obj.getClass.getClassLoader - - // Impl classes are private - private def cls = classLoader.loadClass(className) - - protected def invokeMethod[T](name: String): T = - try - val method = cls.getMethod(name) - method.invoke(obj).asInstanceOf[T] - catch case e: InvocationTargetException => throw e.getCause - - protected def isInstanceOf(className: String): Boolean = classLoader.loadClass(className).isInstance(obj) - - override def toString: String = obj.toString diff --git a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiClass.scala b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiClass.scala new file mode 100644 index 0000000..408a49e --- /dev/null +++ b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiClass.scala @@ -0,0 +1,43 @@ +package ch.epfl.scala.decoder.jdi + +import ch.epfl.scala.decoder.binary.* + +import java.util as ju +import scala.jdk.CollectionConverters.* +import scala.util.control.NonFatal + +/* A class or interface */ +class JdiClass(ref: com.sun.jdi.ReferenceType) extends JdiType(ref) with ClassType: + override def classLoader: BinaryClassLoader = JdiClassLoader(ref.classLoader) + + override def superclass: Option[ClassType] = ref match + case cls: com.sun.jdi.ClassType => Some(JdiClass(cls.superclass)) + case _ => None + + override def interfaces: Seq[ClassType] = ref match + case cls: com.sun.jdi.ClassType => cls.interfaces.asScala.toSeq.map(JdiClass.apply) + case interface: com.sun.jdi.InterfaceType => interface.superinterfaces.asScala.toSeq.map(JdiClass.apply) + + override def isInterface = ref.isInstanceOf[com.sun.jdi.InterfaceType] + + override def sourceLines: Option[SourceLines] = Some(SourceLines(sourceName, allLineLocations.map(_.lineNumber))) + + override def method(name: String, sig: String): Option[Method] = + visibleMethods.find(_.signedName == SignedName(name, sig)) + + override def declaredMethod(name: String, sig: String): Option[Method] = + declaredMethods.find(_.signedName == SignedName(name, sig)) + + override def declaredMethods: Seq[Method] = ref.methods.asScala.map(JdiMethod(_)).toSeq + + override def declaredField(name: String): Option[Field] = None + + private[jdi] def constantPool: ConstantPool = ConstantPool(ref.constantPool) + + private def allLineLocations: Seq[com.sun.jdi.Location] = + try ref.allLineLocations.asScala.toSeq + catch case e: com.sun.jdi.AbsentInformationException => Seq.empty + + private[jdi] def sourceName: String = ref.sourceName + + private def visibleMethods: Seq[JdiMethod] = ref.visibleMethods.asScala.map(JdiMethod(_)).toSeq diff --git a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiClassLoader.scala b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiClassLoader.scala index de0288a..812f129 100644 --- a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiClassLoader.scala +++ b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiClassLoader.scala @@ -1,16 +1,9 @@ package ch.epfl.scala.decoder.jdi -import ch.epfl.scala.decoder.binary -import ch.epfl.scala.decoder.binary.ClassType - import java.util as ju import scala.jdk.CollectionConverters.* +import ch.epfl.scala.decoder.binary.BinaryClassLoader -class JdiClassLoader(obj: Any) - extends binary.BinaryClassLoader - with JavaReflection(obj, "com.sun.jdi.ClassLoaderReference"): - override def loadClass(name: String): ClassType = - visibleClasses.find(_.name == name).get - - private def visibleClasses: Seq[JdiReferenceType] = - invokeMethod[ju.List[Any]]("visibleClasses").asScala.map(JdiReferenceType.apply(_)).toSeq +class JdiClassLoader(classLoader: com.sun.jdi.ClassLoaderReference) extends BinaryClassLoader: + override def loadClass(name: String): JdiClass = + JdiClass(classLoader.visibleClasses.asScala.find(_.name == name).get) diff --git a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiLocalVariable.scala b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiLocalVariable.scala index 3400a38..3f0f5b7 100644 --- a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiLocalVariable.scala +++ b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiLocalVariable.scala @@ -2,7 +2,7 @@ package ch.epfl.scala.decoder.jdi import ch.epfl.scala.decoder.binary.* -class JdiLocalVariable(obj: Any) extends JavaReflection(obj, "com.sun.jdi.LocalVariable") with Parameter: - override def name: String = invokeMethod("name") +class JdiLocalVariable(localVariable: com.sun.jdi.LocalVariable) extends Parameter: + override def name: String = localVariable.name override def sourceLines: Option[SourceLines] = None - override def `type`: Type = JdiType(invokeMethod("type")) + override def `type`: Type = JdiType(localVariable.`type`) diff --git a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiLocation.scala b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiLocation.scala deleted file mode 100644 index 86b1f4d..0000000 --- a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiLocation.scala +++ /dev/null @@ -1,4 +0,0 @@ -package ch.epfl.scala.decoder.jdi - -class JdiLocation(val obj: Any) extends JavaReflection(obj, "com.sun.jdi.Location"): - def lineNumber: Int = invokeMethod[Int]("lineNumber") diff --git a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiMethod.scala b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiMethod.scala index 92affd2..a1c4059 100644 --- a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiMethod.scala +++ b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiMethod.scala @@ -6,40 +6,37 @@ import java.lang.reflect.InvocationTargetException import scala.jdk.CollectionConverters.* import java.util as ju -class JdiMethod(val obj: Any) extends JavaReflection(obj, "com.sun.jdi.Method") with Method: - override def name: String = invokeMethod("name") +class JdiMethod(method: com.sun.jdi.Method) extends Method: + override def name: String = method.name - override def declaringClass: JdiReferenceType = - JdiReferenceType(invokeMethod("declaringType")) + override def declaringClass: JdiClass = JdiClass(method.declaringType) override def allParameters: Seq[Parameter] = - invokeMethod[java.util.List[Object]]("arguments").asScala.toSeq.map(JdiLocalVariable.apply(_)) + method.arguments.asScala.toSeq.map(JdiLocalVariable.apply(_)) override def returnType: Option[Type] = - try Some(JdiType(invokeMethod("returnType"))) - catch case e: Exception if e.getClass.getName == "com.sun.jdi.ClassNotLoadedException" => None + try Some(JdiType(method.returnType)) + catch case e: com.sun.jdi.ClassNotLoadedException => None - override def returnTypeName: String = invokeMethod("returnTypeName") + override def returnTypeName: String = method.returnTypeName - override def isBridge: Boolean = invokeMethod("isBridge") + override def isBridge: Boolean = method.isBridge - override def isStatic: Boolean = invokeMethod("isStatic") + override def isStatic: Boolean = method.isStatic - override def isFinal: Boolean = invokeMethod("isFinal") + override def isFinal: Boolean = method.isFinal - override def isConstructor: Boolean = invokeMethod("isConstructor") + override def isConstructor: Boolean = method.isConstructor override def sourceLines: Option[SourceLines] = Some(SourceLines(declaringClass.sourceName, allLineLocations.map(_.lineNumber))) override def signedName: SignedName = SignedName(name, signature) - override def instructions: Seq[Instruction] = - ByteCodes.parse(bytecodes, declaringClass.constantPool) + override def instructions: Seq[Instruction] = ByteCodes.parse(bytecodes, declaringClass.constantPool) - private def allLineLocations: Seq[JdiLocation] = - invokeMethod[ju.List[Any]]("allLineLocations").asScala.map(JdiLocation.apply(_)).toSeq + private def allLineLocations: Seq[com.sun.jdi.Location] = method.allLineLocations.asScala.toSeq - private def signature: String = invokeMethod("signature") + private def signature: String = method.signature - private def bytecodes: Array[Byte] = invokeMethod("bytecodes") + private def bytecodes: Array[Byte] = method.bytecodes diff --git a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiReferenceType.scala b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiReferenceType.scala deleted file mode 100644 index 57b67b9..0000000 --- a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiReferenceType.scala +++ /dev/null @@ -1,55 +0,0 @@ -package ch.epfl.scala.decoder.jdi - -import ch.epfl.scala.decoder.binary.* - -import java.util as ju -import scala.jdk.CollectionConverters.* -import scala.util.control.NonFatal - -class JdiReferenceType(obj: Any, className: String = "com.sun.jdi.ReferenceType") - extends JdiType(obj, className) - with ClassType: - override def classLoader: BinaryClassLoader = JdiClassLoader(invokeMethod("classLoader")) - override def superclass: Option[ClassType] = if isClass then asClass.superclass else asInterface.superclass - override def interfaces: Seq[ClassType] = if isClass then asClass.interfaces else asInterface.interfaces - def isClass = isInstanceOf("com.sun.jdi.ClassType") - override def isInterface = isInstanceOf("com.sun.jdi.InterfaceType") - override def sourceLines: Option[SourceLines] = - Some(SourceLines(sourceName, allLineLocations.map(_.lineNumber))) - - override def method(name: String, sig: String): Option[Method] = - visibleMethods.find(_.signedName == SignedName(name, sig)) - - override def declaredMethod(name: String, sig: String): Option[Method] = - declaredMethods.find(_.signedName == SignedName(name, sig)) - - override def declaredMethods: Seq[Method] = - invokeMethod[ju.List[Any]]("methods").asScala.map(JdiMethod(_)).toSeq - - override def declaredField(name: String): Option[Field] = None - - def asClass: JdiClassType = JdiClassType(obj) - def asInterface: JdiInterfaceType = JdiInterfaceType(obj) - def constantPool: ConstantPool = ConstantPool(invokeMethod("constantPool")) - - private def allLineLocations: Seq[JdiLocation] = - try invokeMethod[ju.List[Any]]("allLineLocations").asScala.map(JdiLocation(_)).toSeq - catch - case e: Exception if e.getClass.getName == "com.sun.jdi.AbsentInformationException" => - Seq.empty - - private[jdi] def sourceName: String = invokeMethod("sourceName") - - private def visibleMethods: Seq[JdiMethod] = - invokeMethod[ju.List[Any]]("visibleMethods").asScala.map(JdiMethod(_)).toSeq - -class JdiClassType(obj: Any) extends JdiReferenceType(obj, "com.sun.jdi.ClassType"): - override def superclass: Option[ClassType] = Some(JdiReferenceType(invokeMethod[Any]("superclass"))) - override def interfaces: Seq[ClassType] = - invokeMethod[java.util.List[Any]]("interfaces").asScala.toSeq.map(JdiReferenceType(_)) - -class JdiInterfaceType(obj: Any) extends JdiReferenceType(obj, "com.sun.jdi.InterfaceType"): - override def interfaces: Seq[ClassType] = - invokeMethod[java.util.List[Any]]("superinterfaces").asScala.toSeq.map(JdiReferenceType(_)) - - override def superclass: Option[ClassType] = None diff --git a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiType.scala b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiType.scala index 163e524..846e1f7 100644 --- a/src/main/scala/ch/epfl/scala/decoder/jdi/JdiType.scala +++ b/src/main/scala/ch/epfl/scala/decoder/jdi/JdiType.scala @@ -1,6 +1,6 @@ package ch.epfl.scala.decoder.jdi import ch.epfl.scala.decoder.binary.* -class JdiType(obj: Any, className: String = "com.sun.jdi.Type") extends JavaReflection(obj, className) with Type: - override def name: String = invokeMethod("name") +class JdiType(tpe: com.sun.jdi.Type) extends Type: + override def name: String = tpe.name override def sourceLines: Option[SourceLines] = None